Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#3860] Optimize gray routing rule algorithm #3862

Merged
merged 2 commits into from
Jul 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.apache.servicecomb.router.cache.RouterRuleCache;
import org.apache.servicecomb.router.model.PolicyRuleItem;
import org.apache.servicecomb.router.model.RouteItem;
import org.apache.servicecomb.router.model.TagItem;
import org.apache.servicecomb.router.util.VersionCompareUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -56,25 +54,32 @@ protected AbstractRouterDistributor() {

@Override
public List<INSTANCE> distribute(String targetServiceName, List<INSTANCE> list, PolicyRuleItem invokeRule) {
//init LatestVersion
initLatestVersion(targetServiceName, list);

invokeRule.check(
routerRuleCache.getServiceInfoCacheMap().get(targetServiceName).getLatestVersionTag());
invokeRule.check();

// unSetTags instance list
List<INSTANCE> unSetTagInstances = new ArrayList<>();

// get tag list
Map<TagItem, List<INSTANCE>> versionServerMap = getDistributList(targetServiceName, list, invokeRule);
Map<TagItem, List<INSTANCE>> versionServerMap = getDistributList(targetServiceName, list, invokeRule, unSetTagInstances);

if (CollectionUtils.isEmpty(versionServerMap)) {
LOGGER.debug("route management can not match any rule and route the latest version");
return getLatestVersionList(list, targetServiceName);
// rule note matched instance babel, all instance return, select instance for load balancing later
return list;
}

// weight calculation to obtain the next tags instance
TagItem targetTag = getFiltedServerTagItem(invokeRule, targetServiceName);
if (versionServerMap.containsKey(targetTag)) {
if (targetTag != null && versionServerMap.containsKey(targetTag)) {
return versionServerMap.get(targetTag);
}
return getLatestVersionList(list, targetServiceName);

// has weightLess situation
if (invokeRule.isWeightLess() && unSetTagInstances.size() > 0) {
return unSetTagInstances;
}
return list;
}

@Override
Expand All @@ -98,10 +103,7 @@ public TagItem getFiltedServerTagItem(PolicyRuleItem rule, String targetServiceN
* the method getProperties() contains other field that we don't need.
*/
private Map<TagItem, List<INSTANCE>> getDistributList(String serviceName,
List<INSTANCE> list,
PolicyRuleItem invokeRule) {
String latestV = routerRuleCache.getServiceInfoCacheMap().get(serviceName).getLatestVersionTag()
.getVersion();
List<INSTANCE> list, PolicyRuleItem invokeRule, List<INSTANCE> unSetTagInstances) {
Map<TagItem, List<INSTANCE>> versionServerMap = new HashMap<>();
for (INSTANCE instance : list) {
//get server
Expand All @@ -110,6 +112,7 @@ private Map<TagItem, List<INSTANCE>> getDistributList(String serviceName,
TagItem tagItem = new TagItem(getVersion.apply(instance), getProperties.apply(instance));
TagItem targetTag = null;
int maxMatch = 0;
// obtain the rule with the most parameter matches
for (RouteItem entry : invokeRule.getRoute()) {
if (entry.getTagitem() == null){
continue;
Expand All @@ -120,45 +123,17 @@ private Map<TagItem, List<INSTANCE>> getDistributList(String serviceName,
targetTag = entry.getTagitem();
}
}
if (invokeRule.isWeightLess() && getVersion.apply(instance).equals(latestV)) {
TagItem latestVTag = invokeRule.getRoute().get(invokeRule.getRoute().size() - 1)
.getTagitem();
if (!versionServerMap.containsKey(latestVTag)) {
versionServerMap.put(latestVTag, new ArrayList<>());
}
versionServerMap.get(latestVTag).add(instance);
}
if (targetTag != null) {
if (!versionServerMap.containsKey(targetTag)) {
versionServerMap.put(targetTag, new ArrayList<>());
}
versionServerMap.get(targetTag).add(instance);
} else {
// not matched, placed in the unset tag instances collection
unSetTagInstances.add(instance);
}
}
}
return versionServerMap;
}


public void initLatestVersion(String serviceName, List<INSTANCE> list) {
String latestVersion = null;
for (INSTANCE instance : list) {
if (getServerName.apply(instance).equals(serviceName)) {
if (latestVersion == null || VersionCompareUtil
.compareVersion(latestVersion, getVersion.apply(instance)) == -1) {
latestVersion = getVersion.apply(instance);
}
}
}
TagItem tagitem = new TagItem(latestVersion);
routerRuleCache.getServiceInfoCacheMap().get(serviceName).setLatestVersionTag(tagitem);
}

public List<INSTANCE> getLatestVersionList(List<INSTANCE> list, String targetServiceName) {
String latestV = routerRuleCache.getServiceInfoCacheMap().get(targetServiceName)
.getLatestVersionTag().getVersion();
return list.stream().filter(instance ->
getVersion.apply(instance).equals(latestV)
).collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,11 @@ public PolicyRuleItem() {
/**
* if weight is less than 100, fill with minimum version
*
* @param latestVersionTag
*/
public void check(TagItem latestVersionTag) {
public void check() {
if (CollectionUtils.isEmpty(route)) {
throw new RouterIllegalParamException("canary rule list can not be null");
}
if (route.size() == 1) {
route.get(0).setWeight(100);
return;
}
int sum = 0;
for (RouteItem item : route) {
if (item.getWeight() == null) {
Expand All @@ -68,11 +63,8 @@ public void check(TagItem latestVersionTag) {
if (sum > 100) {
LOGGER.warn("canary rule weight sum is more than 100");
} else if (sum < 100) {
if (latestVersionTag == null) {
LOGGER.warn("canary has some error when set default latestVersion");
}
weightLess = true;
route.add(new RouteItem(100 - sum, latestVersionTag));
route.add(new RouteItem(100 - sum, null));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.servicecomb.router;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.servicecomb.router.distribute.RouterDistributor;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class)
public class RouterDistributorFileWeightLessTest {
private static final String TARGET_SERVICE_NAME = "test_server2";

private RouterFilter routerFilter;

private RouterDistributor<ServiceIns> routerDistributor;

@Autowired
public void setRouterFilter(RouterFilter routerFilter) {
this.routerFilter = routerFilter;
}

@Autowired
public void setRouterDistributor(RouterDistributor<ServiceIns> routerDistributor) {
this.routerDistributor = routerDistributor;
}

@Test
public void testDistribute() {
List<ServiceIns> list = initServiceList();
HashMap<String, String> header = new HashMap<>();
List<ServiceIns> listOfServers = routerFilter
.getFilteredListOfServers(list, TARGET_SERVICE_NAME, header, routerDistributor);
Assertions.assertNotNull(listOfServers);
for (ServiceIns server : listOfServers) {
Assertions.assertEquals(TARGET_SERVICE_NAME, server.getServerName());
}
int serverNum1 = 0;
int unSetTagNum = 0;

for (int i = 0; i < 10; i++) {
List<ServiceIns> serverList = routerFilter
.getFilteredListOfServers(list, TARGET_SERVICE_NAME, header, routerDistributor);
for (ServiceIns serviceIns : serverList) {
if ("01".equals(serviceIns.getId())) {
serverNum1++;
} else if ("02".equals(serviceIns.getId())) {
unSetTagNum++;
}
}
}
boolean flag = false;
if (Math.round(unSetTagNum * 1.0 / serverNum1) == 4) {
flag = true;
}
Assertions.assertTrue(flag);
}

List<ServiceIns> initServiceList() {
ServiceIns serviceIns1 = new ServiceIns("01", "test_server2");
ServiceIns serviceIns2 = new ServiceIns("02", "test_server2");
serviceIns1.setVersion("1.0");
serviceIns2.setVersion("2.0");
serviceIns1.addTags("x-group", "red");
serviceIns2.addTags("x-group", "green");
List<ServiceIns> list = new ArrayList<>();
list.add(serviceIns1);
list.add(serviceIns2);
return list;
}
}
7 changes: 7 additions & 0 deletions governance/src/test/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,10 @@ servicecomb:
- weight: 80
tags:
x-group: green

test_server2: | # 服务名
- precedence: 1
route:
- weight: 20
tags:
x-group: red
Loading