Время летит так быстро, пишиspringcloud (десять): сервисный шлюз zuul основные статьиЕще полгода назад, и вот уже в 2018 году мы продолжаем изучать более продвинутые способы использования Zuul.
В прошлой статье в основном был представлен режим использования шлюза Zuul и механизм автоматической переадресации, но на самом деле в Zuul больше сценариев применения, таких как: аутентификация, переадресация трафика, статистика запросов и т.д., эти функции можно реализовать с помощью Zuul.
Ядро Зуула
Фильтр является ядром Zuul и используется для управления внешними службами. Существует четыре жизненных цикла фильтра, а именно "PRE", "ROUTING", "POST" и "ERROR". Весь жизненный цикл можно представить на следующем рисунке.
Большая часть функций Zuul реализована с помощью фильтров, и эти типы фильтров соответствуют типичному жизненному циклу запроса.
- ПРЕД:Этот фильтр вызывается перед маршрутизацией запроса. Мы можем использовать этот фильтр для реализации аутентификации, выбора запрошенных микросервисов в кластере, регистрации отладочной информации и многого другого.
- **МАРШРУТИЗАЦИЯ:** Этот фильтр направляет запросы микросервисам. Этот фильтр используется для структурирования запросов к микросервисам и запроса микросервисов с помощью Apache HttpClient или Netfilx Ribbon.
- **POST:** Этот фильтр выполняется после маршрутизации в микросервис. Такие фильтры можно использовать для добавления стандартных заголовков HTTP к ответам, сбора статистики и метрик, отправки ответов от микросервисов клиентам и многого другого.
- **ОШИБКА:** Выполнять этот фильтр при возникновении ошибок на других этапах. В дополнение к типам фильтров по умолчанию Zuul также позволяет нам создавать собственные типы фильтров. Например, мы можем настроить фильтр типа STATIC, чтобы он генерировал ответ непосредственно в Zuul, не перенаправляя запрос во внутреннюю микрослужбу.
Фильтр реализован по умолчанию в Zuul
Типы | заказ | фильтр | Функция |
---|---|---|---|
pre | -3 | ServletDetectionFilter | Отмечает тип обработки сервлета |
pre | -2 | Servlet30WrapperFilter | Обтекание запросов HttpServletRequest |
pre | -1 | FormBodyWrapperFilter | обертывание тела запроса |
route | 1 | DebugFilter | пометить флаг отладки |
route | 5 | PreDecorationFilter | Обработать контекст запроса для последующего использования |
route | 10 | RibbonRoutingFilter | переадресация запроса serviceId |
route | 100 | SimpleHostRoutingFilter | переадресация запроса URL |
route | 500 | SendForwardFilter | переадресация запроса |
post | 0 | SendErrorFilter | Обработка ошибочных ответов на запросы |
post | 1000 | SendResponseFilter | Обработка обычных ответов на запросы |
Отключить указанный фильтр
Фильтры, которые необходимо отключить, можно настроить в application.yml в формате:
zuul:
FormBodyWrapperFilter:
pre:
disable: true
Пользовательский фильтр
Чтобы реализовать собственный фильтр, вам нужно унаследовать класс ZuulFilter и переопределить 4 метода.
public class MyFilter extends ZuulFilter {
@Override
String filterType() {
return "pre"; //定义filter的类型,有pre、route、post、error四种
}
@Override
int filterOrder() {
return 10; //定义filter的顺序,数字越小表示顺序越高,越先执行
}
@Override
boolean shouldFilter() {
return true; //表示是否需要执行该filter,true表示执行,false表示不执行
}
@Override
Object run() {
return null; //filter需要执行的具体操作
}
}
Пример пользовательского фильтра
Мы предполагаем, что такой сценарий есть, потому что сервисный шлюз отвечает на все внешние запросы.Чтобы избежать потенциальных угроз безопасности, нам необходимо наложить определенные ограничения на запрос.Например, если запрос содержит Токен, запрос будет продолжайте опускаться. Токен вернется напрямую и выдаст подсказку.
Сначала настройте фильтр и проверьте, содержит ли параметр Token в методе run().
public class TokenFilter extends ZuulFilter {
private final Logger logger = LoggerFactory.getLogger(TokenFilter.class);
@Override
public String filterType() {
return "pre"; // 可以在请求被路由之前调用
}
@Override
public int filterOrder() {
return 0; // filter执行顺序,通过数字指定 ,优先级为0,数字越大,优先级越低
}
@Override
public boolean shouldFilter() {
return true;// 是否执行该过滤器,此处为true,说明需要过滤
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
logger.info("--->>> TokenFilter {},{}", request.getMethod(), request.getRequestURL().toString());
String token = request.getParameter("token");// 获取请求的参数
if (StringUtils.isNotBlank(token)) {
ctx.setSendZuulResponse(true); //对请求进行路由
ctx.setResponseStatusCode(200);
ctx.set("isSuccess", true);
return null;
} else {
ctx.setSendZuulResponse(false); //不对其进行路由
ctx.setResponseStatusCode(400);
ctx.setResponseBody("token is empty");
ctx.set("isSuccess", false);
return null;
}
}
}
Добавьте TokenFilter в очередь перехвата запросов и добавьте следующий код в класс запуска:
@Bean
public TokenFilter tokenFilter() {
return new TokenFilter();
}
Это добавляет наш пользовательский фильтр к перехвату запроса.
тестовое задание
Запускаем примеры проектов последовательно:spring-cloud-eureka
,spring-cloud-producer
,spring-cloud-zuul
, эти три проекта являются предыдущими примерами проектов,spring-cloud-zuul
Слегка переделанный.
адрес:http://localhost:8888/spring-cloud-producer/hello?name=neo
, возврат: токен пуст, запрос перехватывается и возвращается.
адрес:http://localhost:8888/spring-cloud-producer/hello?name=neo&token=xx
, возвращает: привет, нео, это первое сообщение, указывающее, что запрос отвечает нормально.
Из приведенного выше примера видно, что мы можем использовать тип фильтра «PRE» для выполнения большого объема работы по проверке.В реальном использовании мы можем комбинировать shiro, oauth2.0 и другие технологии для аутентификации и проверки.
маршрут взорван
Когда в нашей серверной службе возникает исключение, мы не хотим генерировать исключение на самый внешний уровень и ожидаем, что служба автоматически выполнит понижение версии. Зуул оказывает нам такую поддержку. Когда в службе возникает исключение, она напрямую возвращает нашу предустановленную информацию.
Мы используем настраиваемый резервный метод и назначаем его маршруту, чтобы добиться плавной обработки проблемы доступа к маршруту. В основном это реализовано путем наследования интерфейса ZuulFallbackProvider.ZuulFallbackProvider по умолчанию имеет два метода, один из которых используется для указания того, какой сервис должен быть перехвачен фьюзом, а другой — для настройки возвращаемого содержимого.
public interface ZuulFallbackProvider {
/**
* The route this fallback will be used for.
* @return The route the fallback will be used for.
*/
public String getRoute();
/**
* Provides a fallback response.
* @return The fallback response.
*/
public ClientHttpResponse fallbackResponse();
}
Класс реализации сообщает Zuul, за какое определение маршрута он отвечает, реализуя метод getRoute. Метод fallbackResponse сообщает Zuul, какое возвращаемое значение он предоставит для обработки запроса в случае прерывания цепи.
Позже Spring расширил этот класс, обогатил метод возврата и добавил информацию об исключении к возвращаемому содержимому, поэтому в последней версии рекомендуется прямое наследование класса.FallbackProvider
.
Давайте возьмем приведенный выше сервис spring-cloud-producer в качестве примера, чтобы настроить возвращаемое им содержимое прерывателя цепи.
@Component
public class ProducerFallback implements FallbackProvider {
private final Logger logger = LoggerFactory.getLogger(FallbackProvider.class);
//指定要处理的 service。
@Override
public String getRoute() {
return "spring-cloud-producer";
}
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return "OK";
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("The service is unavailable.".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
@Override
public ClientHttpResponse fallbackResponse(Throwable cause) {
if (cause != null && cause.getCause() != null) {
String reason = cause.getCause().getMessage();
logger.info("Excption {}",reason);
}
return fallbackResponse();
}
}
Когда в службе возникает исключение, распечатайте соответствующую информацию об исключении и верните «Служба недоступна».
Запускаем проект spring-cloud-producer-2.В это время в сервисном центре будет два проекта spring-cloud-producer.Перезапускаем проект Zuul. Затем вручную закройте проект spring-cloud-producer-2 и несколько раз посетите адрес:http://localhost:8888/spring-cloud-producer/hello?name=neo&token=xx
, который поочередно возвращает:
hello neo,this is first messge
The service is unavailable.
...
Согласно возвращенным результатам видно, что проект spring-cloud-producer-2 включил прерыватель цепи и возвращает:The service is unavailable.
В настоящее время Zuul поддерживает слияние только на уровне службы и не поддерживает слияние, связанное с URL-адресом.
повторить маршрут
Иногда служба может быть временно недоступна из-за сетевых или других причин. В это время мы надеемся повторить попытку службы еще раз. Zuul также помогает нам реализовать эту функцию, которую необходимо реализовать в сочетании с Spring Retry. Давайте возьмем приведенный выше проект в качестве примера для демонстрации.
Добавить зависимости Spring Retry
Сначала добавьте зависимость Spring Retry в проект spring-cloud-zuul.
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
Открыть Зуул Повторить
Настройте и включите Zuul Retry в файле конфигурации.
#是否开启重试功能
zuul.retryable=true
#对当前服务的重试次数
ribbon.MaxAutoRetries=2
#切换相同Server的次数
ribbon.MaxAutoRetriesNextServer=0
Таким образом мы включаем функцию повтора Зуула.
тестовое задание
Мы модифицировали spring-cloud-producer-2, добавили время в метод hello и вывели параметры в начале запроса.
@RequestMapping("/hello")
public String index(@RequestParam String name) {
logger.info("request two name is "+name);
try{
Thread.sleep(1000000);
}catch ( Exception e){
logger.error(" hello two error",e);
}
return "hello "+name+",this is two messge";
}
Перезапустите проекты spring-cloud-producer-2 и spring-cloud-zuul.
адрес:http://localhost:8888/spring-cloud-producer/hello?name=neo&token=xx
, когда страница возвращается:The service is unavailable.
При просмотре фонового журнала проекта spring-cloud-producer-2 выглядит следующим образом:
2018-01-22 19:50:32.401 INFO 19488 --- [io-9001-exec-14] o.s.c.n.z.f.route.FallbackProvider : request two name is neo
2018-01-22 19:50:33.402 INFO 19488 --- [io-9001-exec-15] o.s.c.n.z.f.route.FallbackProvider : request two name is neo
2018-01-22 19:50:34.404 INFO 19488 --- [io-9001-exec-16] o.s.c.n.z.f.route.FallbackProvider : request two name is neo
Это означает, что было сделано три запроса, то есть две повторных попытки. Это также подтверждает нашу информацию о конфигурации и завершает функцию повторной попытки Zuul.
Уведомление
Включение повторных попыток в некоторых случаях проблематично, например, когда давление слишком велико и один экземпляр перестает отвечать, перенаправляя трафик на другой экземпляр, что, вероятно, в конечном итоге перегрузит все экземпляры. В конце концов, одной из функций автоматического выключателя является предотвращение распространения неисправности или напряжения. При повторной попытке прерыватель цепи срабатывает только в случае сбоя всех экземпляров службы. В настоящее время форма прерывателя цепи больше похожа на предоставление понятного сообщения об ошибке или на то, что служба работает нормально для пользователя.
Без повторных попыток, используя только балансировку нагрузки и прерыватель цепи, вы должны решить, может ли он принять кратковременный прерыватель цепи между отключением одного экземпляра службы и обновлением списка служб с помощью eureka. Если это приемлемо, вам не нужно использовать повторную попытку.
Зуул высокая доступность
Как мы на самом деле используем Zuul, показано на рисунке выше.Разные клиенты используют разные нагрузки для распределения запросов к Zuul на бэкенде.Zuul вызывает бэкэнд-сервис через Eureka и, наконец, выводит его во внешний мир. Таким образом, чтобы обеспечить высокую доступность Zuul, внешний интерфейс может одновременно запускать несколько экземпляров Zuul для загрузки и использовать Nginx или F5 для переадресации нагрузки на внешнем интерфейсе Zuul для достижения высокой доступности.
Ссылаться на:
Сервисный шлюз Spring Cloud (семь) Использование Zuul Filter
Технический анализ Spring Cloud (4) - spring cloud zuul
Использование маршрутизации Zuul