Merge pull request #12 from chuntaojun/2020.0.x

support 2020.0.x
pull/28/head
Haotian Zhang 4 years ago committed by GitHub
commit 9a0b47720c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>2.3.4.RELEASE</version>
<version>3.0.1</version>
<relativePath/>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -71,10 +71,10 @@
<properties>
<!-- Project revision -->
<revision>1.1.4.Hoxton.SR9-SNAPSHOT</revision>
<revision>2020.0.1-SNAPSHOT</revision>
<!-- Spring Cloud -->
<spring.cloud.version>Hoxton.SR9</spring.cloud.version>
<spring.cloud.version>2020.0.1</spring.cloud.version>
<!-- Maven Plugin Versions -->
<jacoco.version>0.8.3</jacoco.version>

@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies-parent</artifactId>
<version>2.3.1.RELEASE</version>
<version>3.0.1</version>
<relativePath/>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -62,7 +62,8 @@
</developers>
<properties>
<revision>1.1.4.Hoxton.SR9-SNAPSHOT</revision>
<revision>2020.0.1-SNAPSHOT</revision>
<guava.version>23.0</guava.version>
<polaris.version>1.2.0</polaris.version>
<tomcat.version>10.0.0-M6</tomcat.version>
<powermock.version>2.0.0</powermock.version>

@ -20,7 +20,7 @@
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
<version>1.2.6</version>
<scope>compile</scope>
</dependency>
<dependency>
@ -53,12 +53,6 @@
<artifactId>feign-okhttp</artifactId>
</dependency>
<!-- 负载均衡依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!-- 熔断超时后,重试机制依赖-->
<dependency>
<groupId>org.springframework.retry</groupId>

@ -12,8 +12,6 @@ global:
timeout: 5s
feign:
hystrix:
enabled: true #在Feign中开启Hystrix
compression:
request:
enabled: false #是否对请求进行GZIP压缩
@ -22,19 +20,5 @@ feign:
response:
enabled: false #是否对响应进行GZIP压缩
ribbon:
polaris:
enabled: true
# 同一实例最大重试次数,不包括首次调用
MaxAutoRetries: 1
# 重试其他实例的最大重试次数不包括首次所选的server
MaxAutoRetriesNextServer: 2
# 是否所有操作都进行重试
OkToRetryOnAllOperations: false
ConnectionTimeout: 1000
ReadTimeout: 1000
eager-load:
enabled: on
serivceB:
url: http://localhost:48081

@ -1,287 +1,194 @@
#描述:全局配置项
global:
#描述系统相关配置
#描述: 系统相关配置
system:
#描述:SDK运行模式
#类型:enum
#范围:0直连模式SDK直接对接server; 1代理模式SDK只对接agent, 通过agent进行server的对接
#默认值:0
mode: 0
#服务发现集群
#描述: 服务发现集群
discoverCluster:
namespace: Polaris
service: polaris.discover
#可选:服务刷新间隔
refreshInterval: 10m
#健康检查集群
#描述: 服务刷新间隔
refreshInterval: 1m
#描述: 是否使用埋点地址作为服务发现集群
sameAsBuiltin: true
#描述: 系统服务使用的路由链
routers:
- metadataRouter
- nearbyBasedRouter
#描述: 系统服务使用的负载均衡方式
lbPolicy: weightedRandom
#描述: 健康检查集群
healthCheckCluster:
namespace: Polaris
service: polaris.healthcheck
#可选:服务刷新间隔
refreshInterval: 10m
#监控上报集群
refreshInterval: 1m
sameAsBuiltin: true
routers:
- metadataRouter
- nearbyBasedRouter
lbPolicy: ringHash
#描述: 监控上报集群
monitorCluster:
namespace: Polaris
service: polaris.monitor
#可选:服务刷新间隔
refreshInterval: 10m
refreshInterval: 1m
sameAsBuiltin: false
routers:
- metadataRouter
- nearbyBasedRouter
lbPolicy: ringHash
#描述: SDK流程数据缓存配置
flowCache:
enable: true
name: simpleCache
expireInterval: 60s
#描述: SDK api调用相关配置
api:
#描述:api超时时间
#类型:string
#格式:^\d+(ms|s|m|h)$
#范围:[1ms:...]
#默认值:1s
#描述: api超时时间
timeout: 5s
#描述:上报间隔
#类型:string
#格式:^\d+(ms|s|m|h)$
#范围:[1ms:...]
#默认值:10m
#描述: 客户端给服务端定时上报自身信息的间隔
reportInterval: 10m
#描述:API因为网络原因调用失败后的重试次数
#类型:int
#范围:[0:...]
#默认值:5
maxRetryTimes: 5
#描述:重试间隔
#类型:string
#格式:^\d+(ms|s|m|h)$
#范围:[1s:...]
#默认值:1s
retryInterval: 1s
#描述:对接polaris server的相关配置
#描述: api因为网络原因调用失败后的最大重试次数
maxRetryTimes: 1
#描述: 2次重试之间的重试间隔
retryInterval: 500ms
#描述: 对接北极星服务端的相关配置
serverConnector:
#描述:访问server的连接协议SDK会根据协议名称会加载对应的插件
#类型:string
#范围:已注册的连接器插件名
#默认值:grpc
#描述:默认服务端埋点接入地址
addresses:
- 127.0.0.1:8091
#描述: 访问server的连接协议SDK会根据协议名称会加载对应的插件
protocol: grpc
#描述:发起连接后的连接超时时间
#类型:string
#格式:^\d+(ms|s|m|h)$
#范围:[1ms:...]
#默认值:200ms
#描述: 发起连接后的连接超时时间
connectTimeout: 500ms
#描述:远程请求超时时间
#类型:string
#格式:^\d+(ms|s|m|h)$
#范围:[1ms:...]
#默认值:1s
#描述: 与服务端发起远程请求超时时间
messageTimeout: 5s
#描述:连接空闲时间长连接模式下当连接空闲超过一定时间后SDK会主动释放连接
#类型:string
#格式:^\d+(ms|s|m|h)$
#范围:[1ms:...]
#默认值:1s
connectionIdleTimeout: 1s
#描述:首次请求的任务队列长度当用户发起首次服务访问请求时SDK会对任务进行队列调度并连接server当积压的任务数超过队列长度后SDK会直接拒绝首次请求的发起。
#类型:int
#范围:[0:...]
#默认值:1000
requestQueueSize: 1000
#描述:server节点的切换周期为了使得server的压力能够均衡SDK会定期针对最新的节点列表进行重新计算自己当前应该连接的节点假如和当前不一致则进行切换
#类型:string
#格式:^\d+(ms|s|m|h)$
#范围:[1m:...]
#默认值:10m
#描述: 连接空闲时间以最后一次消息交互时间来算长连接模式下当连接空闲超过一定时间后SDK会主动释放连接
connectionIdleTimeout: 60s
#描述: server节点的切换周期为了使得server的压力能够均衡SDK会定期切换目标服务端节点
serverSwitchInterval: 10m
plugin:
grpc:
#描述:GRPC客户端单次最大链路接收报文
#类型:int
#范围:(0:524288000]
maxCallRecvMsgSize: 52428800
#统计上报设置
#描述:重连间隔时间
reconnectInterval: 500ms
#描述: 监控及日志数据上报相关配置
statReporter:
#描述是否将统计信息上报至monitor
#类型bool
#默认值true
enable: true
#描述:启用的统计上报插件类型
#类型list
#范围:已经注册的统计上报插件的名字
#默认值stat2Monitor(将信息上报至monitor服务)
chain:
- stat2Monitor
- serviceCache
#描述:统计上报插件配置
#描述: 是否启用上报
enable: false
plugin:
stat2Monitor:
#描述:每次上报多长一段时间的统计信息
#类型:string
#格式:^\d+(ms|s|m|h)$
#范围:[1m:...]
metricsReportWindow: 1m
#描述:将一段时间内的统计信息分为多少个基本单元收集
#类型:int
#范围:[1:...]
#默认值:12
metricsNumBuckets: 12
serviceCache:
#描述:上报缓存信息的周期
reportInterval: 3m
#描述:主调端配置
pushgatewayConfig:
#描述: 上报给prometheus pushgateway的任务名称
jobName: "defaultJob"
#描述: pushgateway的服务名称
serviceName: "polaris.monitor"
#描述: 上报给pushgateway的时间间隔
pushInterval: 10
#描述: 主调端配置
consumer:
#描述:本地缓存相关配置
#描述: 本地服务缓存相关配置
localCache:
#描述:缓存类型
#类型:string
#范围:已注册的本地缓存插件名
#默认值:inmemory基于本机内存的缓存策略
#描述: 缓存插件名
type: inmemory
#描述:服务过期淘汰时间
#类型:string
#格式:^\d+(ms|s|m|h)$
#范围:[1m:...]
#默认值:24h
#描述: 是否启用服务数据缓存
serviceExpireEnable: true
#描述: 服务过期淘汰时间
serviceExpireTime: 24h
#描述:服务定期刷新周期
#类型:string
#格式:^\d+(ms|s|m|h)$
#范围:[1s:...]
#默认值:2s
#描述: 服务定期同步刷新周期
serviceRefreshInterval: 2s
#描述:服务缓存持久化目录SDK在实例数据更新后按照服务维度将数据持久化到磁盘
#类型:string
#格式:本机磁盘目录路径,支持$HOME变量
#默认值:$HOME/polaris/backup
#描述: 是否启用服务数据文件缓存
persistEnable: true
#描述: 服务缓存持久化目录SDK在实例数据更新后按照服务维度将数据持久化到磁盘
persistDir: ./polaris/backup
#描述:缓存写盘失败的最大重试次数
#类型:int
#范围:[1:...]
#默认值:5
persistMaxWriteRetry: 5
#描述:缓存从磁盘读取失败的最大重试次数
#类型:int
#范围:[1:...]
#默认值:1
persistMaxReadRetry: 1
#描述:缓存读写磁盘的重试间隔
#类型:string
#格式:^\d+(ms|s|m|h)$
#范围:[1ms:...]
#默认值:1s
persistRetryInterval: 1s
#描述:服务路由相关配置
#描述: 缓存写盘失败的最大重试次数
persistMaxWriteRetry: 1
#描述: 缓存从磁盘读取失败的最大重试次数
persistMaxReadRetry: 0
#描述: 缓存读写磁盘的重试间隔
persistRetryInterval: 500ms
#描述: 服务路由相关配置
serviceRouter:
# 服务路由链
#描述: 前置路由链
beforeChain:
# 隔离路由
- isolatedRouter
#描述: 服务路由链
chain:
# 基于主调和被调服务规则的路由策略(默认的路由策略)
# 元数据路由
- metadataRouter
# 规则路由
- ruleBasedRouter
# 就近路由策略
# 就近路由
- nearbyBasedRouter
#描述: 后置路由链
afterChain:
# 兜底(全死全活)路由
- recoverRouter
#描述:服务路由插件的配置
plugin:
metadataRouter:
#描述: 元数据路由降级策略。none(不降级), all(降级返回所有的节点), others(降级返回其他节点)
metadataFailOverType: none
nearbyBasedRouter:
#描述:就近路由的最小匹配级别
#类型:string
#范围:region(大区)、zone(区域)、campus(园区)
#默认值:zone
#描述: 就近路由的最小匹配级别。region(大区)、zone(区域)、campus(园区)
matchLevel: zone
ruleBasedRouter: { }
recoverRouter:
#至少应该返回多少比率的实例如果不填默认0%,即全死全活
percentOfMinInstances: 0
#是否开启全死全活,默认开启
enableRecoverAll: true
#描述: 最大匹配级别
maxMatchLevel: all
#描述: 强制就近
strictNearby: false
#描述: 全部实例不健康时是否降级其他地域
enableDegradeByUnhealthyPercent: false
#描述: 达到降级标准的不健康实例百分比
unhealthyPercentToDegrade: 100
#描述: 是否通过上报方式获取地域信息
enableReportLocalAddress: false
#描述:负载均衡相关配置
loadbalancer:
#描述:负载均衡类型
#范围:已注册的负载均衡插件名
#默认值:权重随机负载均衡
#描述: 负载均衡类型(已注册的负载均衡插件名)
type: weightedRandom
plugin:
#描述:虚拟节点的数量
#类型:int
#默认值:500
ringHash:
vnodeCount: 500
#描述:节点熔断相关配置
circuitBreaker:
#描述:是否启用节点熔断功能
#类型:bool
#默认值:true
#描述: 是否启用本地节点熔断功能
enable: true
#描述:实例定时熔断检测周期
#类型:string
#格式:^\d+(ms|s|m|h)$
#范围:[100ms:...]
#默认值:30s
checkPeriod: 100ms
#描述:熔断器半开后最大允许的请求数
#类型:int
#范围:[3:...]
#默认值:10
requestCountAfterHalfOpen: 10
#描述:熔断器打开后,多久后转换为半开状态
#类型:string
#格式:^\d+(ms|s|m|h)$
#范围:[1s:...]
#默认值:30s
sleepWindow: 60s
#描述:熔断器半开到关闭所必须的最少成功请求数
#类型:int
#范围:[1:requestCountAfterHalfOpen]
#默认值:8
successCountAfterHalfOpen: 8
#描述:熔断器半开到关闭的统计周期
#类型:string
#范围:[10s:...]
#默认值:60s
recoverWindow: 60s
#描述:熔断器半开到关闭的统计滑桶数
#类型:int
#范围:[1:...]
#默认值:10
recoverNumBuckets: 10
#描述:熔断策略SDK会根据策略名称加载对应的熔断器插件
#类型:list
#范围:已注册的熔断器插件名
#默认值基于周期连续错误数熔断errorCount、以及基于周期错误率的熔断策略errorRate
#描述: 故障检测周期,根据周期内故障进行熔断
checkPeriod: 1m
#描述: 首次熔断时间,后续熔断时间=重试次数*sleepWindow
sleepWindow: 30s
#描述: 熔断器半开后最大允许的请求数
requestCountAfterHalfOpen: 3
#描述: 熔断器半开到关闭所必须的最少成功请求数
successCountAfterHalfOpen: 3
#描述:熔断策略SDK会根据策略名称加载对应的熔断器插件已注册的熔断器插件名
chain:
- errorCount
- errorRate
#描述:熔断插件配置
#描述: 熔断插件配置
plugin:
#描述:基于周期连续错误数熔断策略配置
errorCount:
#描述:触发连续错误熔断的阈值
#类型:int
#范围:[1:...]
#默认值:10
continuousErrorThreshold: 1
#描述:连续错误数的最小统计单元数量
#类型:int
#范围:[1:...]
#默认值:10
metricNumBuckets: 1
#描述:连续失败的统计周期
#类型:string
#格式:^\d+(ms|s|m|h)$
#范围:[10ms:...]
#默认值:1m
metricStatTimeWindow: 100ms
#描述: 触发连续错误熔断的阈值
continuousErrorThreshold: 10
#描述:基于周期错误率的熔断策略配置
errorRate:
#描述:触发错误率熔断的阈值
#类型:double
#范围:(0:1]
#默认值:0.5
errorRateThreshold: 0.01
#描述:错误率熔断的最小统计单元数量
#类型:int
#范围:[1:...]
#默认值:5
#描述:触发错误率熔断的阈值百分比
errorRateThreshold: 1
#描述: 错误率熔断的滑窗数量
metricNumBuckets: 5
#描述:错误率熔断的统计周期
#类型:string
#格式:^\d+(ms|s|m|h)$
#范围:[1s:...]
#默认值:1m
metricStatTimeWindow: 1s
#描述:触发错误率熔断的最低请求阈值
#类型:int
#范围:(0:...]
#默认值:10
requestVolumeThreshold: 1
#描述: 触发错误率熔断的最低请求阈值
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

@ -20,7 +20,7 @@
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
<version>1.2.6</version>
<scope>compile</scope>
</dependency>
<dependency>
@ -51,12 +51,6 @@
<artifactId>feign-okhttp</artifactId>
</dependency>
<!-- 负载均衡依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!-- 熔断超时后,重试机制依赖-->
<dependency>
<groupId>org.springframework.retry</groupId>

@ -12,8 +12,6 @@ global:
timeout: 5s
feign:
hystrix:
enabled: true #在Feign中开启Hystrix
compression:
request:
enabled: false #是否对请求进行GZIP压缩
@ -22,19 +20,5 @@ feign:
response:
enabled: false #是否对响应进行GZIP压缩
ribbon:
polaris:
enabled: true
# 同一实例最大重试次数,不包括首次调用
MaxAutoRetries: 1
# 重试其他实例的最大重试次数不包括首次所选的server
MaxAutoRetriesNextServer: 2
# 是否所有操作都进行重试
OkToRetryOnAllOperations: false
ConnectionTimeout: 1000
ReadTimeout: 1000
eager-load:
enabled: on
serivceB:
url: http://localhost:48081

@ -42,18 +42,6 @@
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 熔断依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- 负载均衡依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!-- 熔断超时后,重试机制依赖-->
<dependency>
<groupId>org.springframework.retry</groupId>

@ -14,6 +14,11 @@
<name>Polaris Discovery Callee Service</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
<groupId>com.tencent.cloud</groupId>

@ -18,6 +18,11 @@
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
<groupId>com.tencent.cloud</groupId>

@ -18,6 +18,8 @@
package com.tencent.cloud.polaris.discovery.service.caller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@ -37,6 +39,12 @@ public class DiscoveryCallerController {
@Autowired
private DiscoveryCalleeService discoveryCalleeService;
@Configuration
@LoadBalancerClient(value = "service-provider")
class DiscoveryCallerConfiguration {
}
/**
*
*

@ -1,10 +1,11 @@
server:
session-timeout: 1800
port: 48080
spring:
application:
name: DiscoveryCallerService
cloud:
loadbalancer:
configurations: default
polaris:
address: grpc://127.0.0.1:8091
consul:

@ -20,6 +20,7 @@
</modules>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
@ -43,16 +44,9 @@
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 熔断依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- 负载均衡依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- 熔断超时后,重试机制依赖-->

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>polaris-gateway-example</artifactId>
<groupId>com.tencent.cloud</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gateway-zuul-service</artifactId>
<name>Spring Cloud Starter Tencent Polaris Gateway Zuul Example</name>
<dependencies>
<dependency>
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
<groupId>com.tencent.cloud</groupId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-polaris-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>

@ -1,20 +0,0 @@
server:
session-timeout: 1800
port: 48082
spring:
application:
name: GatewayZuulService
cloud:
polaris:
address: grpc://127.0.0.1:8091
tencent:
metadata:
content:
a: 1
transitive:
- a
zuul:
routes:
GatewayCalleeService:
serviceId: GatewayCalleeService
path: /GatewayCalleeService/**

@ -15,7 +15,6 @@
<name>Spring Cloud Starter Tencent Polaris Gateway Example</name>
<modules>
<module>gateway-zuul-service</module>
<module>gateway-scg-service</module>
<module>gateway-callee-service</module>
</modules>
@ -32,19 +31,6 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 熔断依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- 负载均衡依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!-- 熔断超时后,重试机制依赖-->
<dependency>
<groupId>org.springframework.retry</groupId>

@ -13,11 +13,24 @@
<artifactId>ratelimit-callee-service</artifactId>
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.6</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
<groupId>com.tencent.cloud</groupId>
@ -30,5 +43,9 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
</dependencies>
</project>

@ -20,6 +20,8 @@ package com.tencent.cloud.ratelimit.example.service.callee;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@ -43,6 +45,12 @@ public class BusinessController {
@Value("${spring.application.name}")
private String appName;
@Configuration
@LoadBalancerClient(value = "service-provider")
class RateLimitCalleeConfiguration {
}
/**
*

@ -20,6 +20,7 @@ package com.tencent.cloud.ratelimit.example.service.callee;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@ -28,7 +29,7 @@ import org.springframework.web.client.RestTemplate;
* @author Haotian Zhang
*/
@SpringBootApplication
@EnableAutoConfiguration
@EnableDiscoveryClient
public class RateLimitCalleeService {
@Bean

@ -1,11 +1,12 @@
server:
session-timeout: 1800
port: 48081
spring:
application:
name: RateLimitCalleeService
cloud:
loadbalancer:
configurations: default
polaris:
address: grpc://127.0.0.1:8091
discovery:
namespace: Test
namespace: default

@ -52,11 +52,5 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

@ -23,11 +23,11 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.cloud.openfeign.loadbalancer.RetryableFeignBlockingLoadBalancerClient;
/**
* Wrap Spring Bean and decorating proxy for Feign Client.
@ -50,16 +50,14 @@ public class PolarisFeignBeanPostProcessor implements BeanPostProcessor, BeanFac
}
private Object wrapper(Object bean) {
if (isNeedWrap(bean)) {
if (bean instanceof LoadBalancerFeignClient) {
LoadBalancerFeignClient client = ((LoadBalancerFeignClient) bean);
return new PolarisLoadBalancerFeignClient(createPolarisFeignClient(client.getDelegate()), factory(),
clientFactory());
}
if (bean instanceof FeignBlockingLoadBalancerClient) {
FeignBlockingLoadBalancerClient client = (FeignBlockingLoadBalancerClient) bean;
if (bean instanceof RetryableFeignBlockingLoadBalancerClient) {
RetryableFeignBlockingLoadBalancerClient client = (RetryableFeignBlockingLoadBalancerClient) bean;
return new PolarisFeignBlockingLoadBalancerClient(createPolarisFeignClient(client.getDelegate()),
factory.getBean(BlockingLoadBalancerClient.class));
factory.getBean(BlockingLoadBalancerClient.class),
factory.getBean(LoadBalancerProperties.class),
factory.getBean(LoadBalancerClientFactory.class));
}
return createPolarisFeignClient((Client) bean);
}
@ -68,8 +66,7 @@ public class PolarisFeignBeanPostProcessor implements BeanPostProcessor, BeanFac
private boolean isNeedWrap(Object bean) {
return bean instanceof Client && !(bean instanceof PolarisFeignClient)
&& !(bean instanceof PolarisFeignBlockingLoadBalancerClient)
&& !(bean instanceof PolarisLoadBalancerFeignClient);
&& !(bean instanceof PolarisFeignBlockingLoadBalancerClient);
}
private PolarisFeignClient createPolarisFeignClient(Client delegate) {
@ -80,12 +77,4 @@ public class PolarisFeignBeanPostProcessor implements BeanPostProcessor, BeanFac
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.factory = beanFactory;
}
CachingSpringLoadBalancerFactory factory() {
return this.factory.getBean(CachingSpringLoadBalancerFactory.class);
}
SpringClientFactory clientFactory() {
return this.factory.getBean(SpringClientFactory.class);
}
}

@ -18,7 +18,9 @@
package com.tencent.cloud.polaris.circuitbreaker.feign;
import feign.Client;
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
/**
@ -28,9 +30,11 @@ import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalance
*/
public class PolarisFeignBlockingLoadBalancerClient extends FeignBlockingLoadBalancerClient {
public PolarisFeignBlockingLoadBalancerClient(Client delegate,
BlockingLoadBalancerClient loadBalancerClient) {
super(delegate, loadBalancerClient);
LoadBalancerClient loadBalancerClient,
LoadBalancerProperties properties,
LoadBalancerClientFactory loadBalancerClientFactory) {
super(delegate, loadBalancerClient, properties, loadBalancerClientFactory);
}
}

@ -51,9 +51,6 @@ public class PolarisFeignClientTest {
if (client instanceof PolarisFeignClient) {
return;
}
if (client instanceof PolarisLoadBalancerFeignClient) {
return;
}
if (client instanceof PolarisFeignBlockingLoadBalancerClient) {
return;
}

@ -31,12 +31,23 @@
</dependency>
<!-- Spring Cloud Tencent dependencies end -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
<optional>true</optional>
</dependency>
<!-- Polaris dependencies start -->
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-discovery-factory</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-router-factory</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-test-common</artifactId>
@ -50,11 +61,6 @@
</dependency>
<!-- Polaris dependencies end -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
@ -67,6 +73,12 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
@ -90,5 +102,6 @@
<artifactId>powermock-api-mockito2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

@ -69,8 +69,8 @@ public class PolarisProperties {
/**
* 使spring cloud
*/
@Value("${server.port:}")
private int port;
@Value("${server.port:#{-1}}")
private int port = -1;
/**
*
@ -78,14 +78,12 @@ public class PolarisProperties {
@Value("${spring.cloud.polaris.discovery.loadbalancer.enabled:#{true}}")
private Boolean loadbalancerEnabled;
/**
* loadbalnce strategy
*/
@Value("${spring.cloud.polaris.discovery.loadbalancer.policy:#{'weightedRandom'}}")
private String policy;
/**
* loadbalnce strategy
*/

@ -15,23 +15,20 @@
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.circuitbreaker.feign;
package com.tencent.cloud.polaris.loadbalancer;
import feign.Client;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
/**
* Wrap for {@link LoadBalancerFeignClient}
*
* @author Haotian Zhang
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
public class PolarisLoadBalancerFeignClient extends LoadBalancerFeignClient {
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.polaris.enabled", havingValue = "true")
public @interface ConditionalOnLoadBalancerPolaris {
public PolarisLoadBalancerFeignClient(Client delegate,
CachingSpringLoadBalancerFactory lbClientFactory,
SpringClientFactory clientFactory) {
super(delegate, lbClientFactory, clientFactory);
}
}

@ -15,13 +15,11 @@
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.ribbon;
package com.tencent.cloud.polaris.loadbalancer;
import com.tencent.cloud.polaris.discovery.ConditionalOnPolarisDiscoveryEnabled;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.context.annotation.Configuration;
/**
@ -29,9 +27,9 @@ import org.springframework.context.annotation.Configuration;
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnLoadBalancerPolaris
@ConditionalOnPolarisDiscoveryEnabled
@AutoConfigureAfter(RibbonAutoConfiguration.class)
@RibbonClients(defaultConfiguration = PolarisRibbonServerListConfiguration.class)
public class PolarisDiscoveryRibbonAutoConfiguration {
@LoadBalancerClients(defaultConfiguration = PolarisLoadBalancerClientConfiguration.class)
public class LoadBalancerPolarisAutoConfiguration {
}

@ -0,0 +1,93 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.loadbalancer;
import com.tencent.cloud.constant.LoadbalancerConstant;
import com.tencent.cloud.polaris.PolarisProperties;
import com.tencent.polaris.router.api.core.RouterAPI;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
import org.springframework.cloud.client.ConditionalOnReactiveDiscoveryEnabled;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
/**
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
public class PolarisLoadBalancerClientConfiguration {
@Bean
@ConditionalOnMissingBean
public ReactorLoadBalancer<ServiceInstance> polarisLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory,
PolarisProperties polarisProperties,
RouterAPI routerAPI) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new PolarisLoadbalancer(
name, loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),
polarisProperties, routerAPI);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnReactiveDiscoveryEnabled
@Order(LoadbalancerConstant.DISCOVERY_SERVICE_INSTANCE_SUPPLIER_ORDER)
public static class ReactiveSupportConfiguration {
@Bean
@ConditionalOnBean(ReactiveDiscoveryClient.class)
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "default", matchIfMissing = true)
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder().withDiscoveryClient()
.build(context);
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnBlockingDiscoveryEnabled
@Order(LoadbalancerConstant.DISCOVERY_SERVICE_INSTANCE_SUPPLIER_ORDER + 1)
public static class BlockingSupportConfiguration {
@Bean
@ConditionalOnBean(DiscoveryClient.class)
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "default", matchIfMissing = true)
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient()
.build(context);
}
}
}

@ -0,0 +1,114 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.loadbalancer;
import com.tencent.cloud.metadata.context.MetadataContextHolder;
import com.tencent.cloud.polaris.PolarisProperties;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.tencent.cloud.polaris.pojo.PolarisServiceInstance;
import com.tencent.polaris.api.pojo.DefaultServiceInstances;
import com.tencent.polaris.api.pojo.Instance;
import com.tencent.polaris.api.pojo.ServiceInstances;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.api.rpc.Criteria;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;
/**
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
public class PolarisLoadbalancer extends RoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private static final Logger log = LoggerFactory.getLogger(PolarisLoadbalancer.class);
private final String serviceId;
private ObjectProvider<ServiceInstanceListSupplier> supplierObjectProvider;
private final PolarisProperties discoveryProperties;
private final RouterAPI routerAPI;
public PolarisLoadbalancer(String serviceId,
ObjectProvider<ServiceInstanceListSupplier> supplierObjectProvider,
PolarisProperties discoveryProperties, RouterAPI routerAPI) {
super(supplierObjectProvider, serviceId);
this.serviceId = serviceId;
this.supplierObjectProvider = supplierObjectProvider;
this.discoveryProperties = discoveryProperties;
this.routerAPI = routerAPI;
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
if (!discoveryProperties.getLoadbalancerEnabled()) {
return super.choose(request);
}
ServiceInstanceListSupplier supplier = supplierObjectProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get().next().map(this::getInstanceResponse);
}
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> serviceInstances) {
if (serviceInstances.isEmpty()) {
log.warn("No servers available for service: " + this.serviceId);
return new EmptyResponse();
}
ProcessLoadBalanceRequest request = new ProcessLoadBalanceRequest();
request.setDstInstances(convertToPolarisServiceInstances(serviceInstances));
request.setLbPolicy(discoveryProperties.getPolicy());
request.setCriteria(new Criteria());
try {
ProcessLoadBalanceResponse response = routerAPI.processLoadBalance(request);
return new DefaultResponse(new PolarisServiceInstance(response.getTargetInstance()));
}
catch (Exception e) {
log.warn("PolarisLoadbalancer error", e);
return new EmptyResponse();
}
}
private static ServiceInstances convertToPolarisServiceInstances(List<ServiceInstance> serviceInstances) {
ServiceKey serviceKey = new ServiceKey(MetadataContextHolder.LOCAL_NAMESPACE, serviceInstances.get(0).getServiceId());
List<Instance> polarisInstances = serviceInstances
.stream()
.map(serviceInstance -> ((PolarisServiceInstance) serviceInstance).getPolarisInstance())
.collect(Collectors.toList());
return new DefaultServiceInstances(serviceKey, polarisInstances);
}
}

@ -23,6 +23,7 @@ import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegis
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
@ -43,9 +44,10 @@ public class PolarisAutoServiceRegistration extends AbstractAutoServiceRegistrat
@Override
protected PolarisRegistration getRegistration() {
if (this.registration.getPort() <= 0) {
if (this.registration.getPort() < 0 && this.getPort().get() > 0) {
this.registration.setPort(this.getPort().get());
}
Assert.isTrue(this.registration.getPort() > 0, "service.port has not been set");
return this.registration;
}

@ -175,16 +175,13 @@ public class PolarisServiceRegistry implements ServiceRegistry<Registration> {
* @param heartbeatRequest
*/
public void heartbeat(InstanceHeartbeatRequest heartbeatRequest) {
heartbeatExecutor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try {
polarisDiscoveryHandler.getProviderAPI().heartbeat(heartbeatRequest);
} catch (PolarisException e) {
log.error("polaris heartbeat[{}]", e.getCode(), e);
} catch (Exception e) {
log.error("polaris heartbeat runtime error", e);
}
heartbeatExecutor.scheduleWithFixedDelay(() -> {
try {
polarisDiscoveryHandler.getProviderAPI().heartbeat(heartbeatRequest);
} catch (PolarisException e) {
log.error("polaris heartbeat[{}]", e.getCode(), e);
} catch (Exception e) {
log.error("polaris heartbeat runtime error", e);
}
}, 0, ttl, TimeUnit.SECONDS);
}

@ -1,42 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.ribbon;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Haotian Zhang, Andrew Shan, Jie Cheng
*/
@Configuration
public class PolarisRibbonServerListConfiguration {
@Bean
@ConditionalOnMissingBean
public ServerList<Server> ribbonServerList(PolarisDiscoveryHandler polarisDiscoveryHandler,
IClientConfig iClientConfig) {
PolarisServerList serverList = new PolarisServerList(polarisDiscoveryHandler);
serverList.initWithNiwsConfig(iClientConfig);
return serverList;
}
}

@ -1,74 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.ribbon;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractServerList;
import com.netflix.loadbalancer.Server;
import com.tencent.cloud.polaris.pojo.PolarisServer;
import com.tencent.polaris.api.pojo.Instance;
import com.tencent.polaris.api.pojo.ServiceInstances;
import com.tencent.polaris.api.rpc.InstancesResponse;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler;
import java.util.ArrayList;
import java.util.List;
/**
* @author Haotian Zhang, Andrew Shan, Jie Cheng
*/
public class PolarisServerList extends AbstractServerList<Server> {
private String serviceId;
private PolarisDiscoveryHandler polarisDiscoveryHandler;
public PolarisServerList(PolarisDiscoveryHandler polarisDiscoveryHandler) {
this.polarisDiscoveryHandler = polarisDiscoveryHandler;
}
@Override
public List<Server> getInitialListOfServers() {
return getServers();
}
@Override
public List<Server> getUpdatedListOfServers() {
return getServers();
}
private List<Server> getServers() {
InstancesResponse filteredInstances = polarisDiscoveryHandler.getInstances(serviceId);
ServiceInstances serviceInstances = filteredInstances.toServiceInstances();
List<Server> polarisServers = new ArrayList<>();
for (Instance instance : serviceInstances.getInstances()) {
polarisServers.add(new PolarisServer(serviceInstances, instance));
}
return polarisServers;
}
public String getServiceId() {
return serviceId;
}
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
this.serviceId = iClientConfig.getClientName();
}
}

@ -1,5 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration,\
com.tencent.cloud.polaris.ribbon.PolarisDiscoveryRibbonAutoConfiguration,\
com.tencent.cloud.polaris.registry.PolarisServiceRegistryAutoConfiguration

@ -22,8 +22,6 @@ import static com.tencent.polaris.test.common.Consts.PORT;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.assertj.core.api.Assertions.assertThat;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler;
import org.junit.Test;
@ -40,7 +38,6 @@ public class PolarisRibbonServerListAutoConfigurationTest {
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(
PolarisRibbonClientTest.class,
PolarisDiscoveryClientConfiguration.class))
.withPropertyValues("spring.application.name=" + SERVICE_PROVIDER)
.withPropertyValues("server.port=" + PORT)
@ -48,36 +45,4 @@ public class PolarisRibbonServerListAutoConfigurationTest {
.withPropertyValues("spring.cloud.polaris.discovery.namespace=" + NAMESPACE_TEST)
.withPropertyValues("spring.cloud.polaris.discovery.token=xxxxxx");
@Test
public void testProperties() {
this.contextRunner.run(context -> {
PolarisDiscoveryHandler discoveryHandler = context.getBean(PolarisDiscoveryHandler.class);
PolarisServerList serverList = new PolarisServerList(discoveryHandler);
IClientConfig iClientConfig = context.getBean(IClientConfig.class);
serverList.initWithNiwsConfig(iClientConfig);
assertThat(serverList.getServiceId()).isEqualTo(SERVICE_PROVIDER);
});
}
@Configuration
@EnableAutoConfiguration
@EnableDiscoveryClient
static class PolarisRibbonClientTest {
@Bean
IClientConfig iClientConfig() {
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
config.setClientName(SERVICE_PROVIDER);
return config;
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
}

@ -24,13 +24,10 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.Server;
import com.tencent.cloud.polaris.context.PolarisContextConfiguration;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler;
import java.util.List;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.test.mock.discovery.NamingServer;
@ -75,56 +72,6 @@ public class PolarisServerListTest {
}
}
/**
* Test {@link PolarisServerList#getInitialListOfServers()} with empty server list.
*/
@Test
@SuppressWarnings("unchecked")
public void test1(){
this.contextRunner.run(context -> {
// mock
IClientConfig iClientConfig = mock(IClientConfig.class);
when(iClientConfig.getClientName()).thenReturn(SERVICE_PROVIDER);
PolarisDiscoveryHandler polarisDiscoveryHandler = context.getBean(PolarisDiscoveryHandler.class);
PolarisServerList serverList = new PolarisServerList(polarisDiscoveryHandler);
serverList.initWithNiwsConfig(iClientConfig);
List<Server> servers = serverList.getInitialListOfServers();
assertThat(servers).isEmpty();
});
}
/**
* Test {@link PolarisServerList#getUpdatedListOfServers()} with server list of size 3.
*/
@Test
@SuppressWarnings("unchecked")
public void test2() throws Exception {
this.contextRunner.run(context -> {
// mock
IClientConfig iClientConfig = mock(IClientConfig.class);
when(iClientConfig.getClientName()).thenReturn(SERVICE_PROVIDER);
PolarisDiscoveryHandler polarisDiscoveryHandler = context.getBean(PolarisDiscoveryHandler.class);
PolarisServerList serverList = new PolarisServerList(polarisDiscoveryHandler);
serverList.initWithNiwsConfig(iClientConfig);
// add service with 3 instances
NamingService.InstanceParameter instanceParameter = new NamingService.InstanceParameter();
instanceParameter.setHealthy(true);
instanceParameter.setIsolated(false);
instanceParameter.setWeight(100);
ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, SERVICE_PROVIDER);
namingServer.getNamingService().batchAddInstances(serviceKey, PORT, 3, instanceParameter);
List<Server> servers = serverList.getUpdatedListOfServers();
assertThat(servers).hasSize(3);
assertThat(servers.get(0).getPort()).isEqualTo(PORT);
assertThat(servers.get(1).getPort()).isEqualTo(PORT + 1);
assertThat(servers.get(2).getPort()).isEqualTo(PORT + 2);
});
}
@Configuration
@EnableAutoConfiguration
@EnableDiscoveryClient

@ -67,12 +67,6 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>

@ -31,6 +31,12 @@
</dependency>
<!-- Spring Cloud Tencent dependencies end -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
<optional>true</optional>
</dependency>
<!-- Polaris dependencies start -->
<dependency>
<groupId>com.tencent.polaris</groupId>
@ -43,12 +49,6 @@
<scope>test</scope>
</dependency>
<!-- Polaris dependencies end -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

@ -0,0 +1,112 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.router;
import com.tencent.cloud.constant.LoadbalancerConstant;
import com.tencent.cloud.polaris.router.PolarisRouterServiceInstanceListSupplier;
import com.tencent.polaris.api.exception.PolarisException;
import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.factory.api.RouterAPIFactory;
import com.tencent.polaris.router.api.core.RouterAPI;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled;
import org.springframework.cloud.client.ConditionalOnReactiveDiscoveryEnabled;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
/**
* Auto-configuration Ribbon for Polaris.
*
* @author Haotian Zhang
*/
@Configuration()
@EnableConfigurationProperties
@ConditionalOnProperty(value = "spring.cloud.polaris.loadbalancer.enabled", matchIfMissing = true)
public class PolarisRouterAutoConfiguration {
@Bean(name = "polarisRoute")
@ConditionalOnMissingBean
public RouterAPI polarisRouter(SDKContext polarisContext) throws PolarisException {
return RouterAPIFactory.createRouterAPIByContext(polarisContext);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnReactiveDiscoveryEnabled
@Order(LoadbalancerConstant.ROUTER_SERVICE_INSTANCE_SUPPLIER_ORDER - 1000)
public static class ReactiveSupportConfiguration {
// @Bean
// @ConditionalOnBean(ReactiveDiscoveryClient.class)
// @ConditionalOnMissingBean
// @ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "default", matchIfMissing = true)
// public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(RouterAPI routerAPI,
// ConfigurableApplicationContext context) {
// return new PolarisRouterServiceInstanceListSupplier(ServiceInstanceListSupplier.builder().withDiscoveryClient()
// .build(context), routerAPI);
// }
@Bean
@ConditionalOnBean(ReactiveDiscoveryClient.class)
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "zone-preference", matchIfMissing = true)
public ServiceInstanceListSupplier polarisRouterDiscoveryClientServiceInstanceListSupplier(RouterAPI routerAPI,
ConfigurableApplicationContext context) {
return new PolarisRouterServiceInstanceListSupplier(ServiceInstanceListSupplier.builder().withDiscoveryClient()
.withZonePreference()
.build(context), routerAPI);
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnBlockingDiscoveryEnabled
@Order(LoadbalancerConstant.ROUTER_SERVICE_INSTANCE_SUPPLIER_ORDER + 1 - 1000)
public static class BlockingSupportConfiguration {
// @Bean
// @ConditionalOnBean(DiscoveryClient.class)
// @ConditionalOnMissingBean
// @ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "default", matchIfMissing = true)
// public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
// RouterAPI routerAPI,
// ConfigurableApplicationContext context) {
// return new PolarisRouterServiceInstanceListSupplier(ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient()
// .build(context), routerAPI);
// }
@Bean
@ConditionalOnBean(DiscoveryClient.class)
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "zone-preference", matchIfMissing = true)
public ServiceInstanceListSupplier polarisRouterDiscoveryClientServiceInstanceListSupplier(RouterAPI routerAPI,
ConfigurableApplicationContext context) {
return new PolarisRouterServiceInstanceListSupplier(ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient()
.withZonePreference().build(context), routerAPI);
}
}
}

@ -17,16 +17,9 @@
package com.tencent.cloud.polaris.router;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.DynamicServerListLoadBalancer;
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.PollingServerListUpdater;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import com.tencent.cloud.metadata.constant.MetadataConstant.SystemMetadataKey;
import com.tencent.cloud.metadata.context.MetadataContextHolder;
import com.tencent.cloud.polaris.pojo.PolarisServer;
import com.tencent.cloud.polaris.pojo.PolarisServiceInstance;
import com.tencent.polaris.api.pojo.DefaultInstance;
import com.tencent.polaris.api.pojo.DefaultServiceInstances;
import com.tencent.polaris.api.pojo.Instance;
@ -36,64 +29,63 @@ import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.router.api.core.RouterAPI;
import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest;
import com.tencent.polaris.router.api.rpc.ProcessRoutersResponse;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.loadbalancer.core.DelegatingServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Flux;
/**
* @author Haotian Zhang
*/
public class PolarisRoutingLoadBalancer extends DynamicServerListLoadBalancer<Server> {
public class PolarisRouterServiceInstanceListSupplier extends DelegatingServiceInstanceListSupplier {
private final RouterAPI routerAPI;
public PolarisRoutingLoadBalancer(IClientConfig config, IRule rule, IPing ping,
ServerList<Server> serverList, RouterAPI routerAPI) {
super(config, rule, ping, serverList, null, new PollingServerListUpdater());
public PolarisRouterServiceInstanceListSupplier(ServiceInstanceListSupplier delegate, RouterAPI routerAPI) {
super(delegate);
this.routerAPI = routerAPI;
}
@Override
public List<Server> getReachableServers() {
List<Server> allServers = super.getAllServers();
public Flux<List<ServiceInstance>> get() {
return getDelegate().get().map(this::chooseInstances);
}
@Override
public Flux<List<ServiceInstance>> get(Request request) {
return super.get(request);
}
public List<ServiceInstance> chooseInstances(List<ServiceInstance> allServers) {
if (CollectionUtils.isEmpty(allServers)) {
return allServers;
}
ServiceInstances serviceInstances = null;
if (allServers.get(0) instanceof PolarisServer) {
serviceInstances = ((PolarisServer) allServers.get(0)).getServiceInstances();
} else {
String serviceName;
// notice the difference between different service registries
if (StringUtils.isNotBlank(allServers.get(0).getMetaInfo().getServiceIdForDiscovery())) {
serviceName = allServers.get(0).getMetaInfo().getServiceIdForDiscovery();
} else {
serviceName = allServers.get(0).getMetaInfo().getAppName();
}
if (StringUtils.isBlank(serviceName)) {
throw new IllegalStateException(
"PolarisRoutingLoadBalancer only Server with AppName or ServiceIdForDiscovery attribute");
}
ServiceKey serviceKey = new ServiceKey(MetadataContextHolder.LOCAL_NAMESPACE, serviceName);
List<Instance> instances = new ArrayList<>(8);
for (Server server : allServers) {
DefaultInstance instance = new DefaultInstance();
instance.setNamespace(MetadataContextHolder.LOCAL_NAMESPACE);
instance.setService(serviceName);
instance.setHealthy(server.isAlive());
instance.setProtocol(server.getScheme());
instance.setId(server.getId());
instance.setHost(server.getHost());
instance.setPort(server.getPort());
instance.setZone(server.getZone());
instance.setWeight(100);
instances.add(instance);
}
serviceInstances = new DefaultServiceInstances(serviceKey, instances);
String serviceName = allServers.get(0).getServiceId();
if (StringUtils.isBlank(serviceName)) {
throw new IllegalStateException(
"PolarisRoutingLoadBalancer only Server with AppName or ServiceIdForDiscovery attribute");
}
ServiceKey serviceKey = new ServiceKey(MetadataContextHolder.LOCAL_NAMESPACE, serviceName);
List<Instance> instances = new ArrayList<>(8);
for (ServiceInstance server : allServers) {
DefaultInstance instance = new DefaultInstance();
instance.setNamespace(MetadataContextHolder.LOCAL_NAMESPACE);
instance.setService(serviceName);
instance.setProtocol(server.getScheme());
instance.setId(server.getInstanceId());
instance.setHost(server.getHost());
instance.setPort(server.getPort());
instance.setWeight(100);
instances.add(instance);
}
serviceInstances = new DefaultServiceInstances(serviceKey, instances);
ProcessRoutersRequest processRoutersRequest = new ProcessRoutersRequest();
processRoutersRequest.setDstInstances(serviceInstances);
String srcNamespace = MetadataContextHolder.get().getSystemMetadata(SystemMetadataKey.LOCAL_NAMESPACE);
@ -110,15 +102,10 @@ public class PolarisRoutingLoadBalancer extends DynamicServerListLoadBalancer<Se
}
ProcessRoutersResponse processRoutersResponse = routerAPI.processRouters(processRoutersRequest);
ServiceInstances filteredServiceInstances = processRoutersResponse.getServiceInstances();
List<Server> filteredInstances = new ArrayList<>();
List<ServiceInstance> filteredInstances = new ArrayList<>();
for (Instance instance : filteredServiceInstances.getInstances()) {
filteredInstances.add(new PolarisServer(serviceInstances, instance));
filteredInstances.add(new PolarisServiceInstance(instance));
}
return filteredInstances;
}
@Override
public List<Server> getAllServers() {
return getReachableServers();
}
}

@ -1,56 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.router.config;
import com.tencent.polaris.api.exception.PolarisException;
import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.factory.api.RouterAPIFactory;
import com.tencent.polaris.router.api.core.RouterAPI;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Auto-configuration Ribbon for Polaris.
*
* @author Haotian Zhang
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnProperty(value = "spring.cloud.polaris.loadbalancer.enabled", matchIfMissing = true)
@AutoConfigureAfter(RibbonAutoConfiguration.class)
@RibbonClients(defaultConfiguration = PolarisRibbonClientConfiguration.class)
public class PolarisRibbonAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public PolarisRibbonProperties polarisRibbonProperties() {
return new PolarisRibbonProperties();
}
@Bean(name = "polarisRoute")
@ConditionalOnMissingBean
public RouterAPI polarisRouter(SDKContext polarisContext) throws PolarisException {
return RouterAPIFactory.createRouterAPIByContext(polarisContext);
}
}

@ -1,56 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.router.config;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import com.tencent.cloud.polaris.router.PolarisRoutingLoadBalancer;
import com.tencent.cloud.polaris.router.rule.PolarisLoadBalanceRule;
import com.tencent.cloud.polaris.router.rule.PolarisWeightedRandomRule;
import com.tencent.polaris.router.api.core.RouterAPI;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Haotian Zhang
*/
@Configuration
public class PolarisRibbonClientConfiguration {
@Bean
@ConditionalOnMissingBean
public IRule polarisRibbonRule(PolarisRibbonProperties polarisRibbonProperties) {
switch (PolarisLoadBalanceRule.fromStrategy(polarisRibbonProperties.getPolicy())) {
case WEIGHTED_RANDOM_RULE:
default:
return new PolarisWeightedRandomRule();
}
}
@Bean
@ConditionalOnMissingBean
public ILoadBalancer polarisRoutingLoadBalancer(IClientConfig iClientConfig, IRule iRule, IPing iPing,
ServerList<Server> serverList, RouterAPI polarisRouter) {
return new PolarisRoutingLoadBalancer(iClientConfig, iRule, iPing, serverList, polarisRouter);
}
}

@ -1,64 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.router.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author Haotian Zhang
*/
@ConfigurationProperties("spring.cloud.polaris.ribbon")
public class PolarisRibbonProperties {
/**
*
*/
@Value("${spring.cloud.polaris.loadbalancer.enabled:#{true}}")
private Boolean loadbalancerEnabled;
/**
* loadbalnce strategy
*/
@Value("${spring.cloud.polaris.loadbalancer.strategy:#{'weightedRandom'}}")
private String policy;
public String getPolicy() {
return policy;
}
public void setPolicy(String policy) {
this.policy = policy;
}
public Boolean getLoadbalancerEnabled() {
return loadbalancerEnabled;
}
public void setLoadbalancerEnabled(Boolean loadbalancerEnabled) {
this.loadbalancerEnabled = loadbalancerEnabled;
}
@Override
public String toString() {
return "PolarisRibbonProperties{" +
"loadbalancerEnabled=" + loadbalancerEnabled +
", policy='" + policy + '\'' +
'}';
}
}

@ -1,53 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.router.rule;
import java.util.Arrays;
/**
* @author Haotian Zhang
*/
public enum PolarisLoadBalanceRule {
/**
*
*/
WEIGHTED_RANDOM_RULE("weighted_random");
/**
*
*/
String policy;
PolarisLoadBalanceRule(String strategy) {
this.policy = strategy;
}
public static PolarisLoadBalanceRule fromStrategy(String strategy) {
return Arrays.stream(values()).filter(t -> t.getPolicy().equals(strategy)).findAny()
.orElse(WEIGHTED_RANDOM_RULE);
}
/**
* {@link #policy}getter
*/
public String getPolicy() {
return policy;
}
}

@ -1,67 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.router.rule;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.Server;
import com.tencent.cloud.polaris.pojo.PolarisServer;
import com.tencent.polaris.api.config.consumer.LoadBalanceConfig;
import com.tencent.polaris.api.pojo.Instance;
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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import java.util.List;
/**
* @author Haotian Zhang
*/
public class PolarisWeightedRandomRule extends AbstractLoadBalancerRule {
private static final String POLICY = LoadBalanceConfig.LOAD_BALANCE_WEIGHTED_RANDOM;
@Autowired
private RouterAPI polarisRouter;
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
@Override
public Server choose(Object key) {
List<Server> allServers = getLoadBalancer().getReachableServers();
if (CollectionUtils.isEmpty(allServers)) {
return null;
}
Server server = allServers.get(0);
if (!(server instanceof PolarisServer)) {
throw new IllegalStateException("PolarisDiscoveryRule only support PolarisServer instances");
}
PolarisServer polarisServer = (PolarisServer) server;
ProcessLoadBalanceRequest request = new ProcessLoadBalanceRequest();
request.setDstInstances(polarisServer.getServiceInstances());
request.setLbPolicy(POLICY);
ProcessLoadBalanceResponse processLoadBalanceResponse = polarisRouter.processLoadBalance(request);
Instance targetInstance = processLoadBalanceResponse.getTargetInstance();
return new PolarisServer(polarisServer.getServiceInstances(), targetInstance);
}
}

@ -1,2 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tencent.cloud.polaris.router.config.PolarisRibbonAutoConfiguration
com.tencent.cloud.polaris.router.PolarisRouterAutoConfiguration

@ -17,6 +17,7 @@
package com.tencent.cloud.polaris.router.config;
import com.tencent.cloud.polaris.router.PolarisRouterAutoConfiguration;
import com.tencent.polaris.router.api.core.RouterAPI;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
@ -36,19 +37,11 @@ public class PolarisRibbonAutoConfigurationTest {
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(
PolarisRibbonTest.class,
PolarisRibbonAutoConfiguration.class))
PolarisRouterAutoConfiguration.class))
.withPropertyValues("spring.application.name=" + SERVICE_PROVIDER)
.withPropertyValues("server.port=" + PORT)
.withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081");
@Test
public void testDefaultInitialization() {
this.contextRunner.run(context -> {
assertThat(context).hasSingleBean(RouterAPI.class);
assertThat(context).hasSingleBean(PolarisRibbonProperties.class);
});
}
@Configuration
@EnableAutoConfiguration
static class PolarisRibbonTest {

@ -42,15 +42,11 @@
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.google.guava</groupId>-->
<!-- <artifactId>guava</artifactId>-->
<!-- <version>${guava.version}</version>-->
<!-- </dependency>-->
<dependency>
<groupId>commons-collections</groupId>

@ -15,21 +15,20 @@
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.gateway.example.zuul;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
package com.tencent.cloud.constant;
/**
* @author Haotian Zhang
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
@SpringBootApplication
@EnableZuulProxy
public class GatewayZuulApplication {
public final class LoadbalancerConstant {
private LoadbalancerConstant() {}
public static final int DISCOVERY_SERVICE_INSTANCE_SUPPLIER_ORDER = 193827465 - 1000000;
public static void main(String[] args) {
SpringApplication.run(GatewayZuulApplication.class, args);
}
/**
* We need to ensure that the routing action is performed before the load balancing action can be performed
*/
public static final int ROUTER_SERVICE_INSTANCE_SUPPLIER_ORDER = DISCOVERY_SERVICE_INSTANCE_SUPPLIER_ORDER - 1000000;
}

@ -1,109 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.pojo;
import com.google.common.base.Objects;
import com.netflix.loadbalancer.Server;
import com.tencent.polaris.api.pojo.Instance;
import com.tencent.polaris.api.pojo.ServiceInstances;
import org.apache.commons.lang.StringUtils;
import java.util.Map;
/**
* Polaris implementation of {@link Server}
*
* @author Haotian Zhang
*/
public class PolarisServer extends Server {
private final ServiceInstances serviceInstances;
private final Instance instance;
private final MetaInfo metaInfo;
public PolarisServer(ServiceInstances serviceInstances, Instance instance) {
super(instance.getHost(), instance.getPort());
if (StringUtils.equalsIgnoreCase(instance.getProtocol(), "https")) {
setSchemea("https");
} else {
setSchemea("http");
}
this.serviceInstances = serviceInstances;
this.instance = instance;
this.metaInfo = new MetaInfo() {
@Override
public String getAppName() {
return instance.getService();
}
@Override
public String getServerGroup() {
return null;
}
@Override
public String getServiceIdForDiscovery() {
return instance.getService();
}
@Override
public String getInstanceId() {
return instance.getId();
}
};
}
public Instance getInstance() {
return instance;
}
@Override
public MetaInfo getMetaInfo() {
return metaInfo;
}
public Map<String, String> getMetadata() {
return instance.getMetadata();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
PolarisServer that = (PolarisServer) o;
return Objects.equal(instance, that.instance);
}
@Override
public int hashCode() {
return Objects.hashCode(super.hashCode(), instance);
}
public ServiceInstances getServiceInstances() {
return serviceInstances;
}
}

@ -48,6 +48,10 @@ public class PolarisServiceInstance implements ServiceInstance {
}
}
public Instance getPolarisInstance() {
return instance;
}
@Override
public String getInstanceId() {
return ServiceInstance.super.getInstanceId();

@ -22,13 +22,16 @@ import feign.Contract;
import feign.Feign;
import feign.InvocationHandlerFactory;
import feign.Target;
import feign.hystrix.FallbackFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.cloud.openfeign.FeignClientFactoryBean;
import org.springframework.cloud.openfeign.FeignContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.util.StringUtils;
import java.lang.reflect.InvocationHandler;
@ -85,7 +88,10 @@ public class PluggableFeign {
super.invocationHandlerFactory(new InvocationHandlerFactory() {
@Override
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
Object feignClientFactoryBean = applicationContext.getBean("&" + target.type().getName());
GenericApplicationContext gctx = (GenericApplicationContext) Builder.this.applicationContext;
BeanDefinition def = gctx.getBeanDefinition(target.type().getName());
FeignClientFactoryBean feignClientFactoryBean = (FeignClientFactoryBean) def.getAttribute("feignClientsRegistrarFactoryBean");
Class fallback = (Class) ReflectionUtils.getFieldValue(feignClientFactoryBean, "fallback");
Class fallbackFactory = (Class) ReflectionUtils.getFieldValue(feignClientFactoryBean,
@ -104,8 +110,7 @@ public class PluggableFeign {
if (void.class != fallback) {
fallbackInstance = getFallbackInstanceFromContext(beanName, "fallback", fallback,
target.type());
return new PluggableFeignInvocationHandler(target, dispatch,
new FallbackFactory.Default<>(fallbackInstance), pluggableFeignPlugins);
return new PluggableFeignInvocationHandler(target, dispatch, new FallbackFactory.Default(fallbackInstance), pluggableFeignPlugins);
}
if (void.class != fallbackFactory) {

@ -20,7 +20,7 @@ package com.tencent.cloud.feign;
import feign.FeignException;
import feign.InvocationHandlerFactory;
import feign.Target;
import feign.hystrix.FallbackFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import java.lang.reflect.Method;
import java.util.Map;
@ -36,7 +36,7 @@ public class PluggableFeignContext {
private Map<Method, InvocationHandlerFactory.MethodHandler> dispatch;
private FallbackFactory fallbackFactory;
private FallbackFactory<?> fallbackFactory;
private Map<Method, Method> fallbackMethodMap;
@ -66,11 +66,11 @@ public class PluggableFeignContext {
this.dispatch = dispatch;
}
public FallbackFactory getFallbackFactory() {
public FallbackFactory<?> getFallbackFactory() {
return fallbackFactory;
}
public void setFallbackFactory(FallbackFactory fallbackFactory) {
public void setFallbackFactory(FallbackFactory<?> fallbackFactory) {
this.fallbackFactory = fallbackFactory;
}

@ -20,9 +20,9 @@ package com.tencent.cloud.feign;
import feign.FeignException;
import feign.InvocationHandlerFactory;
import feign.Target;
import feign.hystrix.FallbackFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.util.CollectionUtils;
import java.lang.reflect.InvocationHandler;
@ -48,7 +48,7 @@ public class PluggableFeignInvocationHandler implements InvocationHandler {
private final Map<Method, InvocationHandlerFactory.MethodHandler> dispatch;
private FallbackFactory fallbackFactory;
private FallbackFactory<?> fallbackFactory;
private Map<Method, Method> fallbackMethodMap;
@ -59,7 +59,7 @@ public class PluggableFeignInvocationHandler implements InvocationHandler {
private List<PluggableFeignPlugin> exceptionPluggableFeignPlugins;
PluggableFeignInvocationHandler(Target<?> target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch,
FallbackFactory fallbackFactory, List<PluggableFeignPlugin> pluggableFeignPlugins) {
FallbackFactory<?> fallbackFactory, List<PluggableFeignPlugin> pluggableFeignPlugins) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch");
this.fallbackFactory = fallbackFactory;
@ -110,7 +110,9 @@ public class PluggableFeignInvocationHandler implements InvocationHandler {
prePlugin.run(context);
}
result = this.dispatch.get(method).invoke(args);
InvocationHandlerFactory.MethodHandler handler = this.dispatch.get(method);
result = handler.invoke(args);
context.setResult(result);

@ -44,12 +44,6 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<scope>test</scope>
</dependency>
<!-- powermock-module-junit4 -->
<dependency>
<groupId>org.powermock</groupId>

@ -30,13 +30,6 @@
<artifactId>spring-cloud-starter-tencent-polaris-ratelimit</artifactId>
</dependency>
<!-- Spring Cloud Tencent dependencies end -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>

@ -17,14 +17,9 @@
package com.tencent.cloud.polaris.gateway.config;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.http.ZuulServlet;
import com.tencent.cloud.polaris.gateway.core.scg.filter.Metadata2HeaderScgFilter;
import com.tencent.cloud.polaris.gateway.core.scg.filter.MetadataFirstScgFilter;
import com.tencent.cloud.polaris.gateway.core.scg.filter.RateLimitScgFilter;
import com.tencent.cloud.polaris.gateway.core.zuul.filter.Metadata2HeaderZuulFilter;
import com.tencent.cloud.polaris.gateway.core.zuul.filter.MetadataFirstZuulFilter;
import com.tencent.cloud.polaris.gateway.core.zuul.filter.RateLimitZuulFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
@ -38,25 +33,7 @@ import org.springframework.context.annotation.Configuration;
@Configuration
public class PolarisGatewayAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ZuulServlet.class)
static class PolarisGatewayZuulAutoConfiguration {
@Bean
public ZuulFilter metadataFirstZuulFilter() {
return new MetadataFirstZuulFilter();
}
@Bean
public ZuulFilter rateLimitZuulFilter() {
return new RateLimitZuulFilter();
}
@Bean
public ZuulFilter metadata2HeaderZuulFilter() {
return new Metadata2HeaderZuulFilter();
}
}
@Configuration(proxyBeanMethods = false)
@Configuration()
@ConditionalOnClass(GlobalFilter.class)
static class PolarisGatewayScgAutoConfiguration {
@Bean

@ -31,7 +31,7 @@ import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;
import static org.springframework.cloud.gateway.filter.LoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER;
import static org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER;
/**
* Scg filter used for writing metadata in HTTP request header.

@ -1,79 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.gateway.core.zuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.tencent.cloud.metadata.constant.MetadataConstant;
import com.tencent.cloud.metadata.context.MetadataContext;
import com.tencent.cloud.metadata.context.MetadataContextHolder;
import com.tencent.cloud.metadata.util.JacksonUtils;
import org.springframework.util.CollectionUtils;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.RIBBON_ROUTING_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.ROUTE_TYPE;
/**
* Zuul filter used for writing metadata in HTTP request header.
*
* @author skyehtzhang
*/
public class Metadata2HeaderZuulFilter extends ZuulFilter {
@Override
public String filterType() {
return ROUTE_TYPE;
}
@Override
public int filterOrder() {
return RIBBON_ROUTING_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
// get request context
RequestContext requestContext = RequestContext.getCurrentContext();
// get metadata of current thread
MetadataContext metadataContext = MetadataContextHolder.get();
// add new metadata and cover old
Map<String, String> customMetadata = metadataContext.getAllTransitiveCustomMetadata();
if (!CollectionUtils.isEmpty(customMetadata)) {
String metadataStr = JacksonUtils.serialize2Json(customMetadata);
try {
requestContext.addZuulRequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA,
URLEncoder.encode(metadataStr, "UTF-8"));
} catch (UnsupportedEncodingException e) {
requestContext.addZuulRequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA, metadataStr);
}
}
return null;
}
}

@ -1,64 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.gateway.core.zuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.tencent.cloud.metadata.constant.MetadataConstant;
import com.tencent.cloud.metadata.context.MetadataContextHolder;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.REQUEST_URI_KEY;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SERVICE_ID_KEY;
/**
* Zuul filter used for setting peer info in context.
*
* @author Haotian Zhang
*/
public class MetadataFirstZuulFilter extends ZuulFilter {
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER + 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
// get request context
RequestContext requestContext = RequestContext.getCurrentContext();
// TODO 对端命名空间暂时与本地命名空间相同
MetadataContextHolder.get().putSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_NAMESPACE,
MetadataContextHolder.get().getSystemMetadata(MetadataConstant.SystemMetadataKey.LOCAL_NAMESPACE));
MetadataContextHolder.get().putSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_SERVICE,
(String) requestContext.get(SERVICE_ID_KEY));
MetadataContextHolder.get().putSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_PATH,
(String) requestContext.get(REQUEST_URI_KEY));
return null;
}
}

@ -1,99 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.gateway.core.zuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.tencent.cloud.metadata.constant.MetadataConstant;
import com.tencent.cloud.metadata.context.MetadataContextHolder;
import com.tencent.cloud.polaris.ratelimit.utils.Consts;
import com.tencent.cloud.polaris.ratelimit.utils.QuotaCheckUtils;
import com.tencent.polaris.ratelimit.api.core.LimitAPI;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashMap;
import java.util.Map;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.RIBBON_ROUTING_FILTER_ORDER;
import static org.springframework.http.HttpStatus.TOO_MANY_REQUESTS;
/**
* Zuul filter used for rate limit.
*
* @author Haotian Zhang
*/
public class RateLimitZuulFilter extends ZuulFilter {
private static final Logger LOG = LoggerFactory.getLogger(RateLimitZuulFilter.class);
@Autowired(required = false)
private LimitAPI limitAPI;
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return RIBBON_ROUTING_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {
return limitAPI != null;
}
@Override
public Object run() {
// get request context
RequestContext requestContext = RequestContext.getCurrentContext();
String peerNamespace =
MetadataContextHolder.get().getSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_NAMESPACE);
String peerService =
MetadataContextHolder.get().getSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_SERVICE);
String peerPath = MetadataContextHolder.get().getSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_PATH);
Map<String, String> labels = null;
if (StringUtils.isNotBlank(peerPath)) {
labels = new HashMap<>();
labels.put("method", peerPath);
}
try {
QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI, peerNamespace, peerService, 1, labels,
null);
if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) {
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(TOO_MANY_REQUESTS.value());
requestContext.getResponse().getWriter().write(Consts.QUOTA_LIMITED_INFO + quotaResponse.getInfo());
}
} catch (Throwable throwable) {
//限流API调用出现异常不应该影响业务流程的调用
LOG.error("fail to rate limit with QuotaRequest[{}-{}-{}].", peerNamespace, peerService, peerPath,
throwable);
}
return null;
}
}
Loading…
Cancel
Save