Commit a725760b by Nepxion

增加基于Region的权重流量策略

parent 2f006b39
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 RegionWeightEntity implements Serializable {
private static final long serialVersionUID = 3356648245119125011L;
private Map<String, Integer> weightMap;
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
......@@ -14,6 +14,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
......@@ -23,6 +24,7 @@ public class WeightFilterEntity implements Serializable {
private static final long serialVersionUID = 7313443273653189837L;
private Map<String, List<WeightEntity>> weightEntityMap = new LinkedHashMap<String, List<WeightEntity>>();
private RegionWeightEntity regionWeightEntity;
public Map<String, List<WeightEntity>> getWeightEntityMap() {
return weightEntityMap;
......@@ -32,6 +34,18 @@ public class WeightFilterEntity implements Serializable {
this.weightEntityMap = weightEntityMap;
}
public RegionWeightEntity getRegionWeightEntity() {
return regionWeightEntity;
}
public void setRegionWeightEntity(RegionWeightEntity regionWeightEntity) {
this.regionWeightEntity = regionWeightEntity;
}
public boolean hasWeight() {
return MapUtils.isNotEmpty(weightEntityMap) || regionWeightEntity != null;
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
......
......@@ -9,20 +9,43 @@ package com.nepxion.discovery.common.util;
* @version 1.0
*/
import java.io.IOException;
import org.apache.commons.lang3.StringUtils;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
public class JsonUtil {
private static ObjectMapper objectMapper;
static {
objectMapper = new ObjectMapper();
// objectMapper.getSerializerProvider().setNullKeySerializer(new NullKeySerializer());
// objectMapper.setDateFormat(new SimpleDateFormat(DiscoveryConstant.DATE_FORMAT));
}
public static class NullKeySerializer extends StdSerializer<Object> {
private static final long serialVersionUID = -9176767187240330396L;
public NullKeySerializer() {
this(null);
}
public NullKeySerializer(Class<Object> object) {
super(object);
}
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeFieldName(StringUtils.EMPTY);
}
}
public static <T> String toJson(T object) {
if (object == null) {
throw new IllegalArgumentException("Object is null");
......
......@@ -40,6 +40,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.nepxion.discovery.common.constant.DiscoveryConstant;
import com.nepxion.discovery.common.entity.CustomizationEntity;
import com.nepxion.discovery.common.entity.DiscoveryEntity;
import com.nepxion.discovery.common.entity.RegionWeightEntity;
import com.nepxion.discovery.common.entity.RouterEntity;
import com.nepxion.discovery.common.entity.RuleEntity;
import com.nepxion.discovery.common.entity.WeightEntity;
......@@ -126,7 +127,7 @@ public class RouterEndpoint {
String region = pluginAdapter.getRegion();
String host = pluginAdapter.getHost();
int port = pluginAdapter.getPort();
int weight = getWeight(serviceId, version);
int weight = getWeight(serviceId, version, region);
Map<String, String> customMap = getCustomMap(serviceId);
String contextPath = pluginAdapter.getContextPath();
......@@ -164,7 +165,7 @@ public class RouterEndpoint {
String region = metadata.get(DiscoveryConstant.REGION);
String host = instance.getHost();
int port = instance.getPort();
int weight = getWeight(routeServiceId, version);
int weight = getWeight(routeServiceId, version, region);
Map<String, String> customMap = getCustomMap(serviceId);
String contextPath = metadata.get(DiscoveryConstant.SPRING_APPLICATION_CONTEXT_PATH);
......@@ -270,7 +271,7 @@ public class RouterEndpoint {
return routerEntityList;
}
private int getWeight(String providerServiceId, String providerVersion) {
private int getWeight(String providerServiceId, String providerVersion, String providerRegion) {
RuleEntity ruleEntity = pluginAdapter.getRule();
if (ruleEntity == null) {
return -1;
......@@ -286,23 +287,35 @@ public class RouterEndpoint {
return -1;
}
Map<String, List<WeightEntity>> weightEntityMap = weightFilterEntity.getWeightEntityMap();
if (MapUtils.isEmpty(weightEntityMap)) {
if (!weightFilterEntity.hasWeight()) {
return -1;
}
Map<String, List<WeightEntity>> weightEntityMap = weightFilterEntity.getWeightEntityMap();
RegionWeightEntity regionWeightEntity = weightFilterEntity.getRegionWeightEntity();
String serviceId = pluginAdapter.getServiceId();
// 取局部的权重配置
int weight = getWeight(serviceId, providerServiceId, providerVersion, weightEntityMap);
// 局部权重配置没找到,取全局的权重配置
if (weight < 0) {
weight = getWeight(null, providerServiceId, providerVersion, weightEntityMap);
weight = getWeight(StringUtils.EMPTY, providerServiceId, providerVersion, weightEntityMap);
}
// 全局的权重配置没找到,取区域的权重配置
if (weight < 0) {
weight = getWeight(providerRegion, regionWeightEntity);
}
return weight;
}
private int getWeight(String consumerServiceId, String providerServiceId, String providerVersion, Map<String, List<WeightEntity>> weightEntityMap) {
if (MapUtils.isEmpty(weightEntityMap)) {
return -1;
}
List<WeightEntity> weightEntityList = weightEntityMap.get(consumerServiceId);
if (CollectionUtils.isEmpty(weightEntityList)) {
return -1;
......@@ -312,6 +325,10 @@ public class RouterEndpoint {
String providerServiceName = weightEntity.getProviderServiceName();
if (StringUtils.equalsIgnoreCase(providerServiceName, providerServiceId)) {
Map<String, Integer> weightMap = weightEntity.getWeightMap();
if (MapUtils.isEmpty(weightMap)) {
return -1;
}
Integer weight = weightMap.get(providerVersion);
if (weight != null) {
return weight;
......@@ -324,6 +341,28 @@ public class RouterEndpoint {
return -1;
}
private int getWeight(String providerRegion, RegionWeightEntity regionWeightEntity) {
if (StringUtils.isEmpty(providerRegion)) {
return -1;
}
if (regionWeightEntity == null) {
return -1;
}
Map<String, Integer> weightMap = regionWeightEntity.getWeightMap();
if (MapUtils.isEmpty(weightMap)) {
return -1;
}
Integer weight = weightMap.get(providerRegion);
if (weight != null) {
return weight;
} else {
return -1;
}
}
private Map<String, String> getCustomMap(String serviceId) {
RuleEntity ruleEntity = pluginAdapter.getRule();
if (ruleEntity == null) {
......
......@@ -15,6 +15,7 @@ public class ConfigConstant {
public static final String DISCOVERY_ELEMENT_NAME = "discovery";
public static final String CUSTOMIZATION_ELEMENT_NAME = "customization";
public static final String SERVICE_ELEMENT_NAME = "service";
public static final String REGION_ELEMENT_NAME = "region";
public static final String BLACKLIST_ELEMENT_NAME = "blacklist";
public static final String WHITELIST_ELEMENT_NAME = "whitelist";
public static final String COUNT_ELEMENT_NAME = "count";
......
......@@ -30,6 +30,7 @@ import com.nepxion.discovery.common.entity.DiscoveryEntity;
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.RegionWeightEntity;
import com.nepxion.discovery.common.entity.RegisterEntity;
import com.nepxion.discovery.common.entity.RuleEntity;
import com.nepxion.discovery.common.entity.VersionEntity;
......@@ -371,8 +372,10 @@ public class XmlConfigParser implements PluginConfigParser {
String consumerServiceName = null;
if (consumerServiceNameAttribute != null) {
consumerServiceName = consumerServiceNameAttribute.getData().toString().trim();
weightEntity.setConsumerServiceName(consumerServiceName);
} else {
consumerServiceName = StringUtils.EMPTY;
}
weightEntity.setConsumerServiceName(consumerServiceName);
Attribute providerServiceNameAttribute = childElement.attribute(ConfigConstant.PROVIDER_SERVICE_NAME_ATTRIBUTE_NAME);
if (providerServiceNameAttribute == null) {
......@@ -407,6 +410,34 @@ public class XmlConfigParser implements PluginConfigParser {
}
weightEntityList.add(weightEntity);
} else if (StringUtils.equals(childElement.getName(), ConfigConstant.REGION_ELEMENT_NAME)) {
RegionWeightEntity regionWeightEntity = weightFilterEntity.getRegionWeightEntity();
if (regionWeightEntity != null) {
throw new DiscoveryException("Allow only one element[" + ConfigConstant.REGION_ELEMENT_NAME + "] to be configed");
}
regionWeightEntity = new RegionWeightEntity();
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 region = 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(region, weight);
}
regionWeightEntity.setWeightMap(weightMap);
weightFilterEntity.setRegionWeightEntity(regionWeightEntity);
}
}
}
......
......@@ -151,4 +151,9 @@ public abstract class AbstractPluginAdapter implements PluginAdapter {
public String getServerVersion(Server server) {
return getServerMetadata(server).get(DiscoveryConstant.VERSION);
}
@Override
public String getServerRegion(Server server) {
return getServerMetadata(server).get(DiscoveryConstant.REGION);
}
}
\ No newline at end of file
......@@ -54,4 +54,6 @@ public interface PluginAdapter {
Map<String, String> getServerMetadata(Server server);
String getServerVersion(Server server);
String getServerRegion(Server server);
}
\ No newline at end of file
......@@ -10,14 +10,12 @@ package com.nepxion.discovery.plugin.framework.decorator;
*/
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.nepxion.discovery.common.entity.WeightEntity;
import com.nepxion.discovery.common.entity.WeightFilterEntity;
import com.nepxion.discovery.plugin.framework.adapter.PluginAdapter;
import com.nepxion.discovery.plugin.framework.loadbalance.WeightRandomLoadBalance;
import com.netflix.loadbalancer.PredicateBasedRule;
......@@ -37,15 +35,15 @@ public abstract class PredicateBasedRuleDecorator extends PredicateBasedRule {
@Override
public Server choose(Object key) {
Map<String, List<WeightEntity>> weightEntityMap = weightRandomLoadBalance.getWeightEntityMap();
if (MapUtils.isEmpty(weightEntityMap)) {
WeightFilterEntity weightFilterEntity = weightRandomLoadBalance.getWeightFilterEntity();
if (!weightFilterEntity.hasWeight()) {
return super.choose(key);
}
List<Server> eligibleServers = getPredicate().getEligibleServers(getLoadBalancer().getAllServers(), key);
try {
return weightRandomLoadBalance.choose(eligibleServers, weightEntityMap);
return weightRandomLoadBalance.choose(eligibleServers, weightFilterEntity);
} catch (Exception e) {
return super.choose(key);
}
......
......@@ -10,14 +10,12 @@ package com.nepxion.discovery.plugin.framework.decorator;
*/
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.nepxion.discovery.common.entity.WeightEntity;
import com.nepxion.discovery.common.entity.WeightFilterEntity;
import com.nepxion.discovery.plugin.framework.adapter.PluginAdapter;
import com.nepxion.discovery.plugin.framework.loadbalance.WeightRandomLoadBalance;
import com.netflix.loadbalancer.Server;
......@@ -37,15 +35,15 @@ public class ZoneAvoidanceRuleDecorator extends ZoneAvoidanceRule {
@Override
public Server choose(Object key) {
Map<String, List<WeightEntity>> weightEntityMap = weightRandomLoadBalance.getWeightEntityMap();
if (MapUtils.isEmpty(weightEntityMap)) {
WeightFilterEntity weightFilterEntity = weightRandomLoadBalance.getWeightFilterEntity();
if (!weightFilterEntity.hasWeight()) {
return super.choose(key);
}
List<Server> eligibleServers = getPredicate().getEligibleServers(getLoadBalancer().getAllServers(), key);
try {
return weightRandomLoadBalance.choose(eligibleServers, weightEntityMap);
return weightRandomLoadBalance.choose(eligibleServers, weightFilterEntity);
} catch (Exception e) {
return super.choose(key);
}
......
......@@ -18,6 +18,7 @@ import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import com.nepxion.discovery.common.entity.DiscoveryEntity;
import com.nepxion.discovery.common.entity.RegionWeightEntity;
import com.nepxion.discovery.common.entity.RuleEntity;
import com.nepxion.discovery.common.entity.WeightEntity;
import com.nepxion.discovery.common.entity.WeightFilterEntity;
......@@ -28,7 +29,7 @@ import com.netflix.loadbalancer.Server;
public class WeightRandomLoadBalance {
private PluginAdapter pluginAdapter;
public Map<String, List<WeightEntity>> getWeightEntityMap() {
public WeightFilterEntity getWeightFilterEntity() {
RuleEntity ruleEntity = pluginAdapter.getRule();
if (ruleEntity == null) {
return null;
......@@ -40,19 +41,11 @@ public class WeightRandomLoadBalance {
}
WeightFilterEntity weightFilterEntity = discoveryEntity.getWeightFilterEntity();
if (weightFilterEntity == null) {
return null;
}
Map<String, List<WeightEntity>> weightEntityMap = weightFilterEntity.getWeightEntityMap();
if (MapUtils.isEmpty(weightEntityMap)) {
return null;
return weightFilterEntity;
}
return weightEntityMap;
}
public Server choose(List<Server> serverList, Map<String, List<WeightEntity>> weightEntityMap) {
public Server choose(List<Server> serverList, WeightFilterEntity weightFilterEntity) {
if (CollectionUtils.isEmpty(serverList)) {
return null;
}
......@@ -60,7 +53,7 @@ public class WeightRandomLoadBalance {
int[] weights = new int[serverList.size()];
for (int i = 0; i < serverList.size(); i++) {
Server server = serverList.get(i);
int weight = getWeight(server, weightEntityMap);
int weight = getWeight(server, weightFilterEntity);
if (weight > 0) {
weights[i] = weight;
}
......@@ -99,26 +92,40 @@ public class WeightRandomLoadBalance {
return weightHolder[0][0];
}
private int getWeight(Server server, Map<String, List<WeightEntity>> weightEntityMap) {
private int getWeight(Server server, WeightFilterEntity weightFilterEntity) {
Map<String, List<WeightEntity>> weightEntityMap = weightFilterEntity.getWeightEntityMap();
RegionWeightEntity regionWeightEntity = weightFilterEntity.getRegionWeightEntity();
String providerServiceId = server.getMetaInfo().getAppName();
String providerVersion = pluginAdapter.getServerVersion(server);
String providerRegion = pluginAdapter.getServerRegion(server);
String serviceId = pluginAdapter.getServiceId();
// 取局部的权重配置
int weight = getWeight(serviceId, providerServiceId, providerVersion, weightEntityMap);
// 局部权重配置没找到,取全局的权重配置
if (weight < 0) {
weight = getWeight(null, providerServiceId, providerVersion, weightEntityMap);
weight = getWeight(StringUtils.EMPTY, providerServiceId, providerVersion, weightEntityMap);
}
// 全局的权重配置没找到,取区域的权重配置
if (weight < 0) {
throw new DiscoveryException("Weight isn't configed for serviceId=" + providerServiceId + ", version=" + providerVersion);
weight = getWeight(providerRegion, regionWeightEntity);
}
if (weight < 0) {
throw new DiscoveryException("Weight isn't configed for serviceId=" + providerServiceId);
}
return weight;
}
private int getWeight(String consumerServiceId, String providerServiceId, String providerVersion, Map<String, List<WeightEntity>> weightEntityMap) {
if (MapUtils.isEmpty(weightEntityMap)) {
return -1;
}
List<WeightEntity> weightEntityList = weightEntityMap.get(consumerServiceId);
if (CollectionUtils.isEmpty(weightEntityList)) {
return -1;
......@@ -128,6 +135,10 @@ public class WeightRandomLoadBalance {
String providerServiceName = weightEntity.getProviderServiceName();
if (StringUtils.equalsIgnoreCase(providerServiceName, providerServiceId)) {
Map<String, Integer> weightMap = weightEntity.getWeightMap();
if (MapUtils.isEmpty(weightMap)) {
return -1;
}
Integer weight = weightMap.get(providerVersion);
if (weight != null) {
return weight;
......@@ -140,6 +151,28 @@ public class WeightRandomLoadBalance {
return -1;
}
private int getWeight(String providerRegion, RegionWeightEntity regionWeightEntity) {
if (StringUtils.isEmpty(providerRegion)) {
return -1;
}
if (regionWeightEntity == null) {
return -1;
}
Map<String, Integer> weightMap = regionWeightEntity.getWeightMap();
if (MapUtils.isEmpty(weightMap)) {
return -1;
}
Integer weight = weightMap.get(providerRegion);
if (weight != null) {
return weight;
} else {
return -1;
}
}
public void setPluginAdapter(PluginAdapter pluginAdapter) {
this.pluginAdapter = pluginAdapter;
}
......
......@@ -92,13 +92,32 @@
"1.1": 10
}
}
],
"": [
{
"consumerServiceName": "",
"providerServiceName": "discovery-springcloud-example-c",
"weightMap": {
"1.0": 80,
"1.1": 20
}
}
]
},
"regionWeightEntity": {
"weightMap": {
"dev": 85,
"qa": 15
}
}
}
},
"customizationEntity": {
"customizationMap": {
"discovery-springcloud-example-b": {
"discovery-springcloud-example-a": {
"database": "qa"
},
"discovery-springcloud-example-c": {
"database": "prod"
}
}
......
......@@ -73,10 +73,13 @@
<!-- 2. 局部配置,即指定consumer-service-name,专门为该消费端配置权重。全局配置,即不指定consumer-service-name,为所有消费端配置相同情形的权重。当局部配置和全局配置同时存在的时候,以局部配置优先 -->
<!-- 3. 尽量为线上所有版本都赋予权重值 -->
<weight>
<!-- 权重流量配置有如下三种方式,粒度由细到粗,优先级分别是由高到底,即先从第一种方式取权重流量值,取不到则到第二种方式取值,再取不到则到第二种方式取值,再取不到则忽略。使用者按照实际情况,选择一种即可 -->
<!-- 表示消费端服务b访问提供端服务c的时候,提供端服务c的1.0版本提供90%的权重流量,1.1版本提供10%的权重流量 -->
<service consumer-service-name="discovery-springcloud-example-b" provider-service-name="discovery-springcloud-example-c" provider-weight-value="1.0=90;1.1=10"/>
<!-- 表示所有消费端服务访问提供端服务c的时候,提供端服务c的1.0版本提供80%的权重流量,1.1版本提供20%的权重流量 -->
<service provider-service-name="discovery-springcloud-example-c" provider-weight-value="1.0=80;1.1=20"/>
<!-- 表示外界调用进来后,区域(region)为dev的服务提供85%的权重流量,区域(region)为qa的服务提供15%的权重流量 -->
<!-- <region provider-weight-value="dev=85;qa=15"/> -->
</weight>
</discovery>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment