мой блогПожалуйста, указывайте первоисточник при перепечатке.
нужно
Новую версию мини-программы необходимо проверить.Если содержимое, возвращаемое новой версией интерфейса, изменилось, серверная часть непосредственно в сети приведет к тому, что старая версия сообщит об ошибке, и она не пройдет проверку, если это не онлайн.
В прошлом совместимость достигалась написанием новых интерфейсов, но было много совместимого кода или избыточного кода, и разработчикам было нелегко об этом подумать.Старые интерфейсы часто модифицировались напрямую, поэтому контроль версий стал срочная потребность.
идеи
Все запросы проходят через шлюз, и естественно думать об управлении версиями на уровне шлюза. Первое, что приходит на ум, этоZuulFilter
Реализовано в фильтре, все фронтенд запросы добавляются в заголовок запросаversion
изheader
, а затем сопоставьте. Однако таким образом можно получить только внешнюю версию, а внутренний экземпляр не может быть сопоставлен.
После запроса данных выясняется, что во время балансировки нагрузки следует реализовать контроль версий. Точно так же все внешние запросы добавляют единицу к заголовку запроса.version
изheader
, серверные экземпляры настроены с версиейtag
.
выполнить
Первое, что нужно отметить, это то, что мой выбор центра управленияconsul
, шлюз естьzuul
.
Стратегия балансировки нагрузки абстрагируется какIRule
интерфейс, который проект использует по умолчаниюIRule
подклассZoneAvoidanceRule extends PredicateBasedRule
, нам нужно реализоватьPredicateBasedRule
подкласс для заменыZoneAvoidanceRule
.
PredicateBasedRule
Нам нужно реализовать метод фильтрации. В этом методе мы реализуем контроль версий. После фильтрации это стратегия балансировки нагрузки по умолчанию. По умолчанию используется опрос.
/**
* Method that provides an instance of {@link AbstractServerPredicate} to be used by this class.
*
*/
public abstract AbstractServerPredicate getPredicate();
VersionPredicate
Мы видим, чтоPredicateBasedRule
изgetPredicate()
Метод должен возвращатьAbstractServerPredicate
实例,这个实例具体定义了版本控制的业务逻辑。 код показывает, как показано ниже:
private static class VersionPredicate extends AbstractServerPredicate {
private static final String VERSION_KEY = "version";
@Override
public boolean apply(@NullableDecl PredicateKey predicateKey) {
if (predicateKey == null) {
return true;
}
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String version = request.getHeader(VERSION_KEY);
if (version == null) {
return true;
}
ConsulServer consulServer = (ConsulServer) predicateKey.getServer();
if (!consulServer.getMetadata().containsKey(VERSION_KEY)) {
return true;
}
return consulServer.getMetadata().get(VERSION_KEY).equals(version);
}
}
Во-первых, давайте разберемся с процессом балансировки нагрузки. После поступления запроса на шлюз будет проанализировано соответствующее имя службы, затем будут получены все доступные экземпляры службы, затем будет вызван наш метод фильтрации, чтобы отфильтровать все экземпляры службы, доступные для запроса, и, наконец, будет выполняться балансировка нагрузки опроса.
PredicateKey
Класс — это экземпляр, в котором будет доступен верхний метод.Server
а такжеloadBalancerKey
Инкапсулированный класс. Бизнес-логика контроля версий выглядит следующим образом:
- судить
predicateKey
Этоnull
, если да, вернуться напрямуюtrue
,true
Указывает, что экземпляр доступен - пройти через
RequestContext
Получить текущий экземпляр запросаHttpServletRequest
, а затем получить номер версии в заголовке запроса через экземпляр запроса - Определите, есть ли у внешнего запроса номер версии, если нет, он не будет версионирован и возвращен напрямую.
true
- Получите экземпляр службы и преобразуйте его в
ConsulServer
класс, вот потому что реестр, который я использую, этоconsul
, Саморегулирование других в соответствующем классе реализации - Определите, установлен ли для экземпляра службы номер версии (например:
spring.cloud.consul.discovery.tags="version=1.0.0"
), вы можете видеть, что мы используемconsul
изtags
Реализован контроль версий, можно устанавливать разныеtag
реализовать множество функций - Точно так же, если для экземпляра службы не установлен номер версии, он вернет напрямую
true
- Наконец, выполните сопоставление версий и верните успешно сопоставленный экземпляр службы.
Примечания
Окончательная реализация выглядит следующим образом:
/**
* @author Yuicon
*/
@Slf4j
public class VersionRule extends PredicateBasedRule {
private final CompositePredicate predicate;
public VersionRule() {
super();
this.predicate = createCompositePredicate(new VersionPredicate(),
new AvailabilityPredicate(this, null));
}
@Override
public AbstractServerPredicate getPredicate() {
return this.predicate;
}
private CompositePredicate createCompositePredicate(VersionPredicate versionPredicate,
AvailabilityPredicate availabilityPredicate) {
return CompositePredicate.withPredicates(versionPredicate, availabilityPredicate)
.build();
}
private static class VersionPredicate extends AbstractServerPredicate {
private static final String VERSION_KEY = "version";
@Override
public boolean apply(@NullableDecl PredicateKey predicateKey) {
if (predicateKey == null) {
return true;
}
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String version = request.getHeader(VERSION_KEY);
if (version == null) {
return true;
}
ConsulServer consulServer = (ConsulServer) predicateKey.getServer();
if (!consulServer.getMetadata().containsKey(VERSION_KEY)) {
return true;
}
log.info("id is {}, header is {}, metadata is {}, result is {}",
consulServer.getMetaInfo().getInstanceId(),
version, consulServer.getMetadata().get(VERSION_KEY),
consulServer.getMetadata().get(VERSION_KEY).equals(version));
return consulServer.getMetadata().get(VERSION_KEY).equals(version);
}
}
}
я изначально добавил@Component
После аннотации сразу прошел локальный тест. Однако после обновления на продакшн сервер вылезла ошибка, что большинство запросов не смогли найти инстанс сервиса, я растерялся и быстро откатился на исходную версию.
Изучив много информации, я нашел статью и обнаружил, что мне нуженConfig
类来声明替换原有的负载均衡策略类。 код показывает, как показано ниже:
@RibbonClients(defaultConfiguration = RibbonGatewayConfig.class)
@Configuration
public class RibbonGatewayConfig {
@Bean
public IRule versionRule() {
return new VersionRule();
}
}
Здесь работает контроль версий.
конец
Есть еще много проблем, обнаруженных в процессе фактического использования. Например, номер версии внешнего интерфейса уникален в глобальном масштабе. Когда одна из служб обновляет номер версии, все службы должны быть обновлены до этого номера версии, даже если код не изменился. Лучшее решение состоит в том, чтобы внешний интерфейс передавал разные номера версий в соответствии с разными службами, но реализовать обратную связь с внешним интерфейсом сложно.
Существует также компромиссное решение, которое заключается в использовании центра конфигурации для настройки включения контроля версий для определенных служб.Поскольку текущий спрос действует только в течение короткого периода времени, требуется контроль версий.После проверки апплета старый экземпляр службы может быть закрыт. Если у вас есть лучшее решение, добро пожаловать на обсуждение.