Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
D
discovery
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
谢捷峰
discovery
Commits
2ec0a00f
Commit
2ec0a00f
authored
Aug 05, 2018
by
Nepxion
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
增加基于随机权重的平滑灰度发布功能
parent
7ad6f973
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
371 additions
and
3 deletions
+371
-3
discovery-common/src/main/java/com/nepxion/discovery/common/entity/DiscoveryEntity.java
+10
-0
discovery-common/src/main/java/com/nepxion/discovery/common/entity/WeightEntity.java
+70
-0
discovery-common/src/main/java/com/nepxion/discovery/common/entity/WeightFilterEntity.java
+54
-0
discovery-plugin-config-center/src/main/java/com/nepxion/discovery/plugin/configcenter/constant/ConfigConstant.java
+4
-0
discovery-plugin-config-center/src/main/java/com/nepxion/discovery/plugin/configcenter/parser/xml/XmlConfigParser.java
+71
-1
discovery-plugin-framework/src/main/java/com/nepxion/discovery/plugin/framework/decorator/ZoneAvoidanceRuleDecorator.java
+147
-0
discovery-plugin-strategy/src/main/java/com/nepxion/discovery/plugin/strategy/discovery/DiscoveryEnabledRule.java
+2
-2
discovery-springcloud-example-service/src/main/resources/rule.xml
+13
-0
No files found.
discovery-common/src/main/java/com/nepxion/discovery/common/entity/DiscoveryEntity.java
View file @
2ec0a00f
...
...
@@ -13,6 +13,7 @@ public class DiscoveryEntity extends FilterHolderEntity {
private
static
final
long
serialVersionUID
=
-
7417362859952278987L
;
private
VersionFilterEntity
versionFilterEntity
;
private
WeightFilterEntity
weightFilterEntity
;
public
DiscoveryEntity
()
{
...
...
@@ -25,4 +26,12 @@ public class DiscoveryEntity extends FilterHolderEntity {
public
void
setVersionFilterEntity
(
VersionFilterEntity
versionFilterEntity
)
{
this
.
versionFilterEntity
=
versionFilterEntity
;
}
public
WeightFilterEntity
getWeightFilterEntity
()
{
return
weightFilterEntity
;
}
public
void
setWeightFilterEntity
(
WeightFilterEntity
weightFilterEntity
)
{
this
.
weightFilterEntity
=
weightFilterEntity
;
}
}
\ No newline at end of file
discovery-common/src/main/java/com/nepxion/discovery/common/entity/WeightEntity.java
0 → 100644
View file @
2ec0a00f
package
com
.
nepxion
.
discovery
.
common
.
entity
;
/**
* <p>Title: Nepxion Discovery</p>
* <p>Description: Nepxion Discovery</p>
* <p>Copyright: Copyright (c) 2017-2050</p>
* <p>Company: Nepxion</p>
* @author Haojun Ren
* @version 1.0
*/
import
java.io.Serializable
;
import
java.util.Map
;
import
org.apache.commons.lang3.builder.EqualsBuilder
;
import
org.apache.commons.lang3.builder.HashCodeBuilder
;
import
org.apache.commons.lang3.builder.ToStringBuilder
;
import
org.apache.commons.lang3.builder.ToStringStyle
;
public
class
WeightEntity
implements
Serializable
{
private
static
final
long
serialVersionUID
=
4242297554671632704L
;
private
String
consumerServiceName
;
private
String
providerServiceName
;
private
Map
<
String
,
Integer
>
weightMap
;
public
WeightEntity
()
{
}
public
String
getConsumerServiceName
()
{
return
consumerServiceName
;
}
public
void
setConsumerServiceName
(
String
consumerServiceName
)
{
this
.
consumerServiceName
=
consumerServiceName
;
}
public
String
getProviderServiceName
()
{
return
providerServiceName
;
}
public
void
setProviderServiceName
(
String
providerServiceName
)
{
this
.
providerServiceName
=
providerServiceName
;
}
public
Map
<
String
,
Integer
>
getWeightMap
()
{
return
weightMap
;
}
public
void
setWeightMap
(
Map
<
String
,
Integer
>
weightMap
)
{
this
.
weightMap
=
weightMap
;
}
@Override
public
int
hashCode
()
{
return
HashCodeBuilder
.
reflectionHashCode
(
this
);
}
@Override
public
boolean
equals
(
Object
object
)
{
return
EqualsBuilder
.
reflectionEquals
(
this
,
object
);
}
@Override
public
String
toString
()
{
return
ToStringBuilder
.
reflectionToString
(
this
,
ToStringStyle
.
MULTI_LINE_STYLE
);
}
}
\ No newline at end of file
discovery-common/src/main/java/com/nepxion/discovery/common/entity/WeightFilterEntity.java
0 → 100644
View file @
2ec0a00f
package
com
.
nepxion
.
discovery
.
common
.
entity
;
/**
* <p>Title: Nepxion Discovery</p>
* <p>Description: Nepxion Discovery</p>
* <p>Copyright: Copyright (c) 2017-2050</p>
* <p>Company: Nepxion</p>
* @author Haojun Ren
* @version 1.0
*/
import
java.io.Serializable
;
import
java.util.LinkedHashMap
;
import
java.util.List
;
import
java.util.Map
;
import
org.apache.commons.lang3.builder.EqualsBuilder
;
import
org.apache.commons.lang3.builder.HashCodeBuilder
;
import
org.apache.commons.lang3.builder.ToStringBuilder
;
import
org.apache.commons.lang3.builder.ToStringStyle
;
public
class
WeightFilterEntity
implements
Serializable
{
private
static
final
long
serialVersionUID
=
7313443273653189837L
;
private
Map
<
String
,
List
<
WeightEntity
>>
weightEntityMap
=
new
LinkedHashMap
<
String
,
List
<
WeightEntity
>>();
public
WeightFilterEntity
()
{
}
public
Map
<
String
,
List
<
WeightEntity
>>
getWeightEntityMap
()
{
return
weightEntityMap
;
}
public
void
setWeightEntityMap
(
Map
<
String
,
List
<
WeightEntity
>>
weightEntityMap
)
{
this
.
weightEntityMap
=
weightEntityMap
;
}
@Override
public
int
hashCode
()
{
return
HashCodeBuilder
.
reflectionHashCode
(
this
);
}
@Override
public
boolean
equals
(
Object
object
)
{
return
EqualsBuilder
.
reflectionEquals
(
this
,
object
);
}
@Override
public
String
toString
()
{
return
ToStringBuilder
.
reflectionToString
(
this
,
ToStringStyle
.
MULTI_LINE_STYLE
);
}
}
\ No newline at end of file
discovery-plugin-config-center/src/main/java/com/nepxion/discovery/plugin/configcenter/constant/ConfigConstant.java
View file @
2ec0a00f
...
...
@@ -18,10 +18,13 @@ public class ConfigConstant {
public
static
final
String
WHITELIST_ELEMENT_NAME
=
"whitelist"
;
public
static
final
String
COUNT_ELEMENT_NAME
=
"count"
;
public
static
final
String
VERSION_ELEMENT_NAME
=
"version"
;
public
static
final
String
WEIGHT_ELEMENT_NAME
=
"weight"
;
public
static
final
String
FILTER_VALUE_ATTRIBUTE_NAME
=
"filter-value"
;
public
static
final
String
SERVICE_NAME_ATTRIBUTE_NAME
=
"service-name"
;
public
static
final
String
CONSUMER_SERVICE_NAME_ATTRIBUTE_NAME
=
"consumer-service-name"
;
public
static
final
String
PROVIDER_SERVICE_NAME_ATTRIBUTE_NAME
=
"provider-service-name"
;
public
static
final
String
CONSUMER_VERSION_VALUE_ATTRIBUTE_NAME
=
"consumer-version-value"
;
public
static
final
String
PROVIDER_VERSION_VALUE_ATTRIBUTE_NAME
=
"provider-version-value"
;
public
static
final
String
PROVIDER_WEIGHT_VALUE_ATTRIBUTE_NAME
=
"provider-weight-value"
;
public
static
final
String
SEPARATE
=
"="
;
}
\ No newline at end of file
discovery-plugin-config-center/src/main/java/com/nepxion/discovery/plugin/configcenter/parser/xml/XmlConfigParser.java
View file @
2ec0a00f
...
...
@@ -12,6 +12,7 @@ package com.nepxion.discovery.plugin.configcenter.parser.xml;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Iterator
;
import
java.util.LinkedHashMap
;
import
java.util.List
;
import
java.util.Map
;
...
...
@@ -25,13 +26,15 @@ import org.slf4j.LoggerFactory;
import
com.nepxion.discovery.common.constant.DiscoveryConstant
;
import
com.nepxion.discovery.common.entity.CountFilterEntity
;
import
com.nepxion.discovery.common.entity.DiscoveryEntity
;
import
com.nepxion.discovery.common.entity.VersionEntity
;
import
com.nepxion.discovery.common.entity.FilterHolderEntity
;
import
com.nepxion.discovery.common.entity.FilterType
;
import
com.nepxion.discovery.common.entity.HostFilterEntity
;
import
com.nepxion.discovery.common.entity.RegisterEntity
;
import
com.nepxion.discovery.common.entity.RuleEntity
;
import
com.nepxion.discovery.common.entity.VersionEntity
;
import
com.nepxion.discovery.common.entity.VersionFilterEntity
;
import
com.nepxion.discovery.common.entity.WeightEntity
;
import
com.nepxion.discovery.common.entity.WeightFilterEntity
;
import
com.nepxion.discovery.common.exception.DiscoveryException
;
import
com.nepxion.discovery.plugin.configcenter.constant.ConfigConstant
;
import
com.nepxion.discovery.plugin.configcenter.parser.xml.dom4j.Dom4JReader
;
...
...
@@ -129,6 +132,8 @@ public class XmlConfigParser implements PluginConfigParser {
parseHostFilter
(
childElement
,
ConfigConstant
.
WHITELIST_ELEMENT_NAME
,
discoveryEntity
);
}
else
if
(
StringUtils
.
equals
(
childElement
.
getName
(),
ConfigConstant
.
VERSION_ELEMENT_NAME
))
{
parseVersionFilter
(
childElement
,
discoveryEntity
);
}
else
if
(
StringUtils
.
equals
(
childElement
.
getName
(),
ConfigConstant
.
WEIGHT_ELEMENT_NAME
))
{
parseWeightFilter
(
childElement
,
discoveryEntity
);
}
}
}
...
...
@@ -295,6 +300,71 @@ public class XmlConfigParser implements PluginConfigParser {
discoveryEntity
.
setVersionFilterEntity
(
versionFilterEntity
);
}
@SuppressWarnings
(
"rawtypes"
)
private
void
parseWeightFilter
(
Element
element
,
DiscoveryEntity
discoveryEntity
)
{
WeightFilterEntity
weightFilterEntity
=
discoveryEntity
.
getWeightFilterEntity
();
if
(
weightFilterEntity
!=
null
)
{
throw
new
DiscoveryException
(
"Allow only one element["
+
ConfigConstant
.
WEIGHT_ELEMENT_NAME
+
"] to be configed"
);
}
weightFilterEntity
=
new
WeightFilterEntity
();
Map
<
String
,
List
<
WeightEntity
>>
weightEntityMap
=
weightFilterEntity
.
getWeightEntityMap
();
for
(
Iterator
elementIterator
=
element
.
elementIterator
();
elementIterator
.
hasNext
();)
{
Object
childElementObject
=
elementIterator
.
next
();
if
(
childElementObject
instanceof
Element
)
{
Element
childElement
=
(
Element
)
childElementObject
;
if
(
StringUtils
.
equals
(
childElement
.
getName
(),
ConfigConstant
.
SERVICE_ELEMENT_NAME
))
{
WeightEntity
weightEntity
=
new
WeightEntity
();
Attribute
consumerServiceNameAttribute
=
childElement
.
attribute
(
ConfigConstant
.
CONSUMER_SERVICE_NAME_ATTRIBUTE_NAME
);
if
(
consumerServiceNameAttribute
==
null
)
{
throw
new
DiscoveryException
(
"Attribute["
+
ConfigConstant
.
CONSUMER_SERVICE_NAME_ATTRIBUTE_NAME
+
"] in element["
+
childElement
.
getName
()
+
"] is missing"
);
}
String
consumerServiceName
=
consumerServiceNameAttribute
.
getData
().
toString
().
trim
();
weightEntity
.
setConsumerServiceName
(
consumerServiceName
);
Attribute
providerServiceNameAttribute
=
childElement
.
attribute
(
ConfigConstant
.
PROVIDER_SERVICE_NAME_ATTRIBUTE_NAME
);
if
(
providerServiceNameAttribute
==
null
)
{
throw
new
DiscoveryException
(
"Attribute["
+
ConfigConstant
.
PROVIDER_SERVICE_NAME_ATTRIBUTE_NAME
+
"] in element["
+
childElement
.
getName
()
+
"] is missing"
);
}
String
providerServiceName
=
providerServiceNameAttribute
.
getData
().
toString
().
trim
();
weightEntity
.
setProviderServiceName
(
providerServiceName
);
Attribute
providerWeightValueAttribute
=
childElement
.
attribute
(
ConfigConstant
.
PROVIDER_WEIGHT_VALUE_ATTRIBUTE_NAME
);
if
(
providerWeightValueAttribute
==
null
)
{
throw
new
DiscoveryException
(
"Attribute["
+
ConfigConstant
.
PROVIDER_WEIGHT_VALUE_ATTRIBUTE_NAME
+
"] in element["
+
childElement
.
getName
()
+
"] is missing"
);
}
String
providerWeightValue
=
providerWeightValueAttribute
.
getData
().
toString
().
trim
();
Map
<
String
,
Integer
>
weightMap
=
new
LinkedHashMap
<
String
,
Integer
>();
List
<
String
>
providerWeightValueList
=
parseList
(
providerWeightValue
);
for
(
String
value
:
providerWeightValueList
)
{
String
[]
valueArray
=
StringUtils
.
split
(
value
,
ConfigConstant
.
SEPARATE
);
String
version
=
valueArray
[
0
].
trim
();
int
weight
=
Integer
.
valueOf
(
valueArray
[
1
].
trim
());
if
(
weight
<
0
)
{
throw
new
DiscoveryException
(
"Attribute["
+
ConfigConstant
.
PROVIDER_WEIGHT_VALUE_ATTRIBUTE_NAME
+
"] in element["
+
childElement
.
getName
()
+
"] has weight value less than 0"
);
}
weightMap
.
put
(
version
,
weight
);
}
weightEntity
.
setWeightMap
(
weightMap
);
List
<
WeightEntity
>
weightEntityList
=
weightEntityMap
.
get
(
consumerServiceName
);
if
(
weightEntityList
==
null
)
{
weightEntityList
=
new
ArrayList
<
WeightEntity
>();
weightEntityMap
.
put
(
consumerServiceName
,
weightEntityList
);
}
weightEntityList
.
add
(
weightEntity
);
}
}
}
discoveryEntity
.
setWeightFilterEntity
(
weightFilterEntity
);
}
private
List
<
String
>
parseList
(
String
value
)
{
if
(
StringUtils
.
isEmpty
(
value
))
{
return
null
;
...
...
discovery-plugin-framework/src/main/java/com/nepxion/discovery/plugin/framework/decorator/ZoneAvoidanceRuleDecorator.java
0 → 100644
View file @
2ec0a00f
package
com
.
nepxion
.
discovery
.
plugin
.
framework
.
decorator
;
/**
* <p>Title: Nepxion Discovery</p>
* <p>Description: Nepxion Discovery</p>
* <p>Copyright: Copyright (c) 2017-2050</p>
* <p>Company: Nepxion</p>
* @author Haojun Ren
* @version 1.0
*/
import
java.util.List
;
import
java.util.Map
;
import
java.util.Random
;
import
org.apache.commons.collections4.CollectionUtils
;
import
org.apache.commons.collections4.MapUtils
;
import
org.apache.commons.lang3.StringUtils
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
com.nepxion.discovery.common.entity.DiscoveryEntity
;
import
com.nepxion.discovery.common.entity.RuleEntity
;
import
com.nepxion.discovery.common.entity.WeightEntity
;
import
com.nepxion.discovery.common.entity.WeightFilterEntity
;
import
com.nepxion.discovery.common.exception.DiscoveryException
;
import
com.nepxion.discovery.plugin.framework.adapter.PluginAdapter
;
import
com.netflix.loadbalancer.Server
;
import
com.netflix.loadbalancer.ZoneAvoidanceRule
;
public
class
ZoneAvoidanceRuleDecorator
extends
ZoneAvoidanceRule
{
private
static
final
Logger
LOG
=
LoggerFactory
.
getLogger
(
ZoneAvoidanceRuleDecorator
.
class
);
@Autowired
private
PluginAdapter
pluginAdapter
;
private
final
Random
random
=
new
Random
();
@Override
public
Server
choose
(
Object
key
)
{
RuleEntity
ruleEntity
=
pluginAdapter
.
getRule
();
if
(
ruleEntity
==
null
)
{
return
super
.
choose
(
key
);
}
DiscoveryEntity
discoveryEntity
=
ruleEntity
.
getDiscoveryEntity
();
if
(
discoveryEntity
==
null
)
{
return
super
.
choose
(
key
);
}
WeightFilterEntity
weightFilterEntity
=
discoveryEntity
.
getWeightFilterEntity
();
if
(
weightFilterEntity
==
null
)
{
return
super
.
choose
(
key
);
}
Map
<
String
,
List
<
WeightEntity
>>
weightEntityMap
=
weightFilterEntity
.
getWeightEntityMap
();
if
(
MapUtils
.
isEmpty
(
weightEntityMap
))
{
return
super
.
choose
(
key
);
}
String
serviceId
=
pluginAdapter
.
getServiceId
();
List
<
WeightEntity
>
weightEntityList
=
weightEntityMap
.
get
(
serviceId
);
if
(
CollectionUtils
.
isEmpty
(
weightEntityList
))
{
return
super
.
choose
(
key
);
}
List
<
Server
>
eligibleServers
=
getPredicate
().
getEligibleServers
(
getLoadBalancer
().
getAllServers
(),
key
);
try
{
return
choose
(
eligibleServers
,
weightEntityList
);
}
catch
(
Exception
e
)
{
return
super
.
choose
(
key
);
}
}
private
Server
choose
(
List
<
Server
>
serverList
,
List
<
WeightEntity
>
weightEntityList
)
{
if
(
CollectionUtils
.
isEmpty
(
serverList
))
{
return
null
;
}
int
[]
weights
=
new
int
[
serverList
.
size
()];
for
(
int
i
=
0
;
i
<
serverList
.
size
();
i
++)
{
Server
server
=
serverList
.
get
(
i
);
int
weight
=
getWeight
(
server
,
weightEntityList
);
if
(
weight
>
0
)
{
weights
[
i
]
=
weight
;
}
}
int
index
=
getIndex
(
weights
);
return
serverList
.
get
(
index
);
}
private
int
getIndex
(
int
[]
weights
)
{
// 次序号/权重区间值
int
[][]
weightHolder
=
new
int
[
weights
.
length
][
2
];
// 总权重
int
totalWeight
=
0
;
// 赋值次序号和区间值累加的数组值,从小到大排列
// 例如,对于权重分别为20,40, 60的三个服务,将形成[0, 20),[20, 60),[60, 120]三个区间
for
(
int
i
=
0
;
i
<
weights
.
length
;
i
++)
{
if
(
weights
[
i
]
<=
0
)
{
continue
;
}
totalWeight
+=
weights
[
i
];
weightHolder
[
i
][
0
]
=
i
;
weightHolder
[
i
][
1
]
=
totalWeight
;
}
// 获取介于0(含)和n(不含)伪随机,均匀分布的int值
int
hitWeight
=
random
.
nextInt
(
totalWeight
)
+
1
;
// [1, totalWeight)
for
(
int
i
=
0
;
i
<
weightHolder
.
length
;
i
++)
{
if
(
hitWeight
<=
weightHolder
[
i
][
1
])
{
return
weightHolder
[
i
][
0
];
}
}
return
weightHolder
[
0
][
0
];
}
private
int
getWeight
(
Server
server
,
List
<
WeightEntity
>
weightEntityList
)
{
String
providerServiceId
=
server
.
getMetaInfo
().
getAppName
();
String
providerVersion
=
pluginAdapter
.
getServerVersion
(
server
);
for
(
WeightEntity
weightEntity
:
weightEntityList
)
{
String
providerServiceName
=
weightEntity
.
getProviderServiceName
();
if
(
StringUtils
.
equalsIgnoreCase
(
providerServiceName
,
providerServiceId
))
{
Map
<
String
,
Integer
>
weightMap
=
weightEntity
.
getWeightMap
();
Integer
weight
=
weightMap
.
get
(
providerVersion
);
if
(
weight
!=
null
)
{
return
weight
;
}
else
{
LOG
.
error
(
"Weight isn't configed for serviceId={}, version={}"
,
providerServiceId
,
providerVersion
);
throw
new
DiscoveryException
(
"Weight isn't configed for serviceId="
+
providerServiceId
+
", version="
+
providerVersion
);
}
}
}
return
-
1
;
}
}
\ No newline at end of file
discovery-plugin-strategy/src/main/java/com/nepxion/discovery/plugin/strategy/discovery/DiscoveryEnabledRule.java
View file @
2ec0a00f
...
...
@@ -11,12 +11,12 @@ package com.nepxion.discovery.plugin.strategy.discovery;
import
org.springframework.util.Assert
;
import
com.nepxion.discovery.plugin.framework.decorator.ZoneAvoidanceRuleDecorator
;
import
com.netflix.loadbalancer.AbstractServerPredicate
;
import
com.netflix.loadbalancer.AvailabilityPredicate
;
import
com.netflix.loadbalancer.CompositePredicate
;
import
com.netflix.loadbalancer.PredicateBasedRule
;
public
class
DiscoveryEnabledRule
extends
PredicateBasedRule
{
public
class
DiscoveryEnabledRule
extends
ZoneAvoidanceRuleDecorator
{
private
final
CompositePredicate
predicate
;
private
final
DiscoveryEnabledPredicate
discoveryEnabledPredicate
;
...
...
discovery-springcloud-example-service/src/main/resources/rule.xml
View file @
2ec0a00f
...
...
@@ -62,5 +62,17 @@
<!-- 表示消费端服务b的1.1,允许访问提供端服务c的1.2版本 -->
<service
consumer-service-name=
"discovery-springcloud-example-b"
provider-service-name=
"discovery-springcloud-example-c"
consumer-version-value=
"1.1"
provider-version-value=
"1.2"
/>
</version>
<!-- 服务发现的多权重灰度访问控制 -->
<!-- service-name,表示服务名 -->
<!-- version-value,表示版本对应的权重值,格式为"版本值=权重值",如果多个用“;”分隔,不允许出现空格 -->
<!-- 权重策略介绍 -->
<!-- 1. 标准配置,举例如下 -->
<!-- <service consumer-service-name="a" provider-service-name="b" provider-weight-value="1.0=70;1.1=30"/> 表示消费端访问提供端的时候,提供端的1.0版本提供70%的权重流量,1.1版本提供30%的权重流量 -->
<!-- 2. 尽量为线上所有版本都赋予权重值 -->
<weight>
<!-- 表示消费端服务b访问提供端服务c的时候,提供端服务c的1.0版本提供70%的权重流量,1.1版本提供30%的权重流量 -->
<service
consumer-service-name=
"discovery-springcloud-example-b"
provider-service-name=
"discovery-springcloud-example-c"
provider-weight-value=
"1.0=70;1.1=30"
/>
</weight>
</discovery>
</rule>
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment