Обработка микросервиса SpringCloud 1 (введение, построение среды, EUREKA)
SpringCloud Microservice Governance II (Robbin, Hystix, Feign)
Управление микросервисом SpringCloud 3 (шлюз Zuul)
9. Зуульские ворота
Благодаря предыдущему обучению в основном формируется архитектура использования Spring Cloud для реализации микросервисов, которая примерно выглядит следующим образом:
В этой архитектуре наш сервисный кластер включает в себя: внутренние сервисы Service A и Service B, которые регистрируются и подписываются на Eureka Server, в то время как Open Service — это внешний сервис, который доступен для вызывающих сервис через балансировку нагрузки. Мы фокусируемся на внешних сервисах и напрямую раскрываем адрес нашего сервиса. Является ли такая реализация разумной или есть лучший способ реализовать ее?
Во-первых, давайте поговорим о некоторых вещах, которые необходимо сделать, и о недостатках этой архитектуры:
- Во-первых, он разрушает природу службы без сохранения состояния.
- Чтобы обеспечить безопасность внешних сервисов, нам нужно реализовать контроль разрешений на доступ к сервису, а механизм контроля разрешений открытых сервисов будет работать и загрязнять бизнес-логику всего открытого сервиса.Самая непосредственная проблема, которую это принесет, это для уничтожения службы.. Безотказная природа REST API в кластерах.
- С точки зрения конкретной разработки и тестирования, помимо собственно бизнес-логики, необходимо также учитывать контрольную обработку доступа к интерфейсу.
- Во-вторых, существующие интерфейсы нельзя повторно использовать напрямую.
- Когда нам нужно получить доступ к существующему интерфейсу в кластере, чтобы получить доступ к внешним службам, мы должны добавить логику проверки к исходному интерфейсу или добавить прокси-вызов для достижения контроля разрешений, и мы не можем напрямую повторно использовать исходный интерфейс.
Столкнувшись с проблемами, подобными описанным выше, как мы можем их решить? Ответ: сервисный шлюз!
Чтобы решить эти проблемы, нам нужно извлечь такие разрешения из нашего сервисного блока, и наиболее подходящей для этих логик является то, где фронтальный доступ, нам нужен более мощный сбалансированный загрузчик Сервисный шлюз.
Сервисный шлюз — неотъемлемая часть архитектуры микросервисов. В процессе предоставления REST API внешней системе через сервисный шлюз, помимо функций сервисной маршрутизации и балансировки нагрузки, он также имеет функции сервисной маршрутизации и балансировки нагрузки.权限控制
и другие функции. Zuul в Spring Cloud Netflix играет такую роль, обеспечивая переднюю защиту для микросервисной архитектуры и в то же время перенося более тяжелое, не связанное с бизнес-логикой, содержимое управления разрешениями на уровень маршрутизации служб, так что основная часть сервисного кластера может иметь более высокую возможность повторного использования и тестируемость.
9.1 Архитектура после добавления Zuul
- Будь то запрос от клиента (ПК или мобильного), или внутренний звонок службы. Все запросы на услуги будут проходить через шлюз Zuul, а затем шлюз будет осуществлять аутентификацию, динамическую маршрутизацию и другие операции. Zuul — это единый вход наших услуг.
9.2 Знакомство с Зуулом
9.3. Быстрый старт
9.3.1. Новый проект
Заполните основную информацию:
Добавьте зависимости Zuul:
9.3.2 Написание классов запуска
пройти через@EnableZuulProxy
Аннотация для включения функций Zuul:
@SpringBootApplication
@EnableZuulProxy // 开启Zuul的网关功能
public class ZuulDemoApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulDemoApplication.class, args);
}
}
9.3.3 Запись конфигурации
server:
port: 10010 #服务端口
spring:
application:
name: api-gateway #指定服务名
9.3.4 Написание правил маршрутизации
Нам нужно использовать Zuul для проксирования службы пользовательского сервиса, сначала посмотрите на статус службы в панели управления:
- IP-адрес: 127.0.0.1
- Порт: 8081
Правила сопоставления:
zuul:
routes:
user-service: # 这里是路由id,随意写
path: /user-service/** # 这里是映射路径
url: http://127.0.0.1:8081 # 映射路径对应的实际url地址
мы встретимсяpath
Все правила запросов ко всем агентамurl
Параметры указанного адреса
В этом примере мы будем/user-service/**
Начало запроса, прокси на http://127.0.0.1:8081
9.3.5 Запустите тест:
Путь доступа должен добавить путь сопоставления правила конфигурации.Мы получаем доступ:http://127.0.0.1:8081/user-service/user/10
9.4 Сервис-ориентированная маршрутизация
В правиле маршрутизации только что мы прописали адрес службы, соответствующий пути! Это явно неразумно, если существует несколько экземпляров одной и той же службы.
Мы должны пойти в реестр Eureka, чтобы найти список всех экземпляров, соответствующих услуге в соответствии с именем службы, а затем выполнить динамическую маршруту!
9.4.1. Добавление клиентских зависимостей Eureka
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
9.4.2 Включить функцию обнаружения клиентов Eureka
@SpringBootApplication
@EnableZuulProxy // 开启Zuul的网关功能
@EnableDiscoveryClient
public class ZuulDemoApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulDemoApplication.class, args);
}
}
9.4.3 Добавьте конфигурацию Eureka для получения служебной информации
eureka:
client:
registry-fetch-interval-seconds: 5 # 获取服务列表的周期:5s
service-url:
defaultZone: http://127.0.0.1:10086/eureka
instance:
prefer-ip-address: true
ip-address: 127.0.0.1
9.4.4. Измените конфигурацию сопоставления и получите ее через имя службы.
Поскольку у нас уже есть клиент Eureka, мы можем получить информацию об адресе службы от Eureka, поэтому нет необходимости указывать IP-адрес при сопоставлении, а нужно получить к нему доступ через имя службы, а в Zuul встроена функция балансировки нагрузки Ribbon. .
zuul:
routes:
user-service: # 这里是路由id,随意写
path: /user-service/** # 这里是映射路径
serviceId: user-service # 指定服务名称
9.4.5 Запуск теста
Начните снова, на этот раз, когда Zuul проксирует, он будет использовать ленту для доступа к балансировке нагрузки:
Журнал показывает, что используется балансировщик нагрузки:
9.5 Упрощенная конфигурация маршрутизации
В конфигурации только что наши правила таковы:
-
zuul.routes.<route>.path=/xxx/**
: указать путь сопоставления.<route>
это пользовательское название маршрута -
zuul.routes.<route>.serviceId=/user-service
: указать имя службы.
В большинстве случаев наши<route>
Имя маршрута часто пишется так же, как и имя службы. Итак, Zuul предоставляет упрощенный синтаксис конфигурации:zuul.routes.<serviceId>=<path>
Например, конфигурация нашего пользовательского сервиса выше может быть упрощена до одной:
zuul:
routes:
user-service: /user-service/** # 这里是映射路径
Конфигурация имени службы опущена.
9.6 Правила маршрутизации по умолчанию
В процессе использования Zuul описанные выше правила значительно упростили элементы настройки. Но когда сервисов много, конфигурация тоже усложняется. Итак, Zuul указывает правила маршрутизации по умолчанию:
- По умолчанию путь сопоставления для всех служб — это имя самой службы.
- Например, имя службы:
user-service
, путь сопоставления по умолчанию:/user-service/**
- Например, имя службы:
То есть, мы можем не настраивать правила сопоставления прямо сейчас.
9.7 Префиксы маршрутизации
Пример конфигурации:
zuul:
prefix: /api # 添加路由前缀
routes:
user-service: # 这里是路由id,随意写
path: /user-service/** # 这里是映射路径
service-id: user-service # 指定服务名称
мы проходимzuul.prefix=/api
чтобы указать префикс маршрута, чтобы при выполнении запроса путь начинался с /api.
дорожка/api/user-service/user/1
будет делегирован/user-service/user/1
9.8 Фильтры
Одной из важных функций Zuul как шлюза является реализация аутентификации запросов. И это действие часто достигается с помощью фильтра, предоставляемого Zuul.
9.8.1.ZuulFilter
ZuulFilter — это родительский класс фильтров верхнего уровня. Здесь мы рассмотрим 4 наиболее важных метода, определенных в нем:
public abstract ZuulFilter implements IZuulFilter{
abstract public String filterType();
abstract public int filterOrder();
boolean shouldFilter();// 来自IZuulFilter
Object run() throws ZuulException;// IZuulFilter
}
-
shouldFilter
: возвращаетBoolean
значение, чтобы определить, нужно ли выполнять фильтр. Верните true, чтобы выполнить, верните false, чтобы не выполнять. -
run
: конкретная бизнес-логика фильтра. -
filterType
: возвращает строку, представляющую тип фильтра. Содержит следующие 4 типа:-
pre
: запрос выполняется перед маршрутизацией -
routing
: вызывается при маршрутизации запроса -
post
: вызывается после маршрутизации и фильтров ошибок -
error
: Произошла ошибка при обработке звонка-запроса
-
-
filterOrder
: определяет порядок выполнения фильтра по возвращаемому значению int, чем меньше число, тем выше приоритет.
9.8.2 Жизненный цикл выполнения фильтра:
Это диаграмма жизненного цикла запроса, представленная на официальном сайте Zuul, которая наглядно показывает последовательность выполнения запроса в каждом фильтре.
- Нормальный процесс:
- При поступлении запроса он сначала проходит через претип фильтр, а затем маршрутизируется тип маршрутизации, запрос поступает к реальному поставщику услуг, выполняет запрос и после возврата результата доходит до постфильтра . Затем верните ответ.
- Процесс исключения:
- В течение всего процесса, если есть исключение в предварительном фильтре или фильтре маршрутизации, оно напрямую попадет в фильтр ошибок.После обработки ошибки запрос будет передан в фильтр POST и, наконец, возвращен пользователю.
- Если у самого фильтра ошибок есть исключение, он в конечном итоге войдет в фильтр POST, а затем вернется.
- Если в фильтре POST есть исключение, оно перейдет к фильтру ошибок, но когда оно отличается от предварительного и маршрутного, запрос больше не попадет в фильтр POST.
Список всех встроенных фильтров:
9.8.3 Сценарии использования
Есть много сценариев:
- Аутентификация запроса: обычно размещается в предварительном типе, если обнаружится, что разрешения на доступ нет, оно будет перехвачено напрямую.
- Обработка исключений: обычно обрабатывается в сочетании с фильтрами типа ошибки и типа сообщения.
- Статистика продолжительности вызова службы: используется в сочетании с предварительным и последующим вызовом.
9.9 Пользовательские фильтры
Далее давайте настроим фильтр для имитации проверки входа. Базовая логика: если в запросе есть параметр маркера доступа, запрос считается действительным и освобождается.
9.9.1 Определение классов фильтров
@Component
public class LoginFilter extends ZuulFilter{
@Override
public String filterType() {
// 登录校验,肯定是在前置拦截
return "pre";
}
@Override
public int filterOrder() {
// 顺序设置为1
return 1;
}
@Override
public boolean shouldFilter() {
// 返回true,代表过滤器生效。
return true;
}
@Override
public Object run() throws ZuulException {
// 登录校验逻辑。
// 1)获取Zuul提供的请求上下文对象
RequestContext ctx = RequestContext.getCurrentContext();
// 2) 从上下文中获取request对象
HttpServletRequest req = ctx.getRequest();
// 3) 从请求中获取token
String token = req.getParameter("access-token");
// 4) 判断
if(token == null || "".equals(token.trim())){
// 没有token,登录校验失败,拦截
ctx.setSendZuulResponse(false);
// 返回401状态码。也可以考虑重定向到登录页。
ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
// 校验通过,可以考虑把用户信息放入上下文,继续向后执行
return null;
}
}
9.10 Балансировка нагрузки и отключение цепи
Zuul по умолчанию интегрировал балансировку нагрузки Ribbon и механизм предохранителей Hystix. Но все политики тайм-аута являются значениями по умолчанию.Например, время тайм-аута предохранителя составляет всего 1 с, что легко активировать. Поэтому рекомендуется настроить его вручную:
zuul:
retryable: true
ribbon:
ConnectTimeout: 250 # 连接超时时间(ms)
ReadTimeout: 2000 # 通信超时时间(ms)
OkToRetryOnAllOperations: true # 是否对所有操作重试
MaxAutoRetriesNextServer: 2 # 同一服务不同实例的重试次数
MaxAutoRetries: 1 # 同一实例的重试次数
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMillisecond: 6000 # 熔断超时时长:6000ms