Три службы микроуправления SpringCloud (шлюз Zuul)

Spring Cloud

Обработка микросервиса SpringCloud 1 (введение, построение среды, EUREKA)
SpringCloud Microservice Governance II (Robbin, Hystix, Feign)
Управление микросервисом SpringCloud 3 (шлюз Zuul)

9. Зуульские ворота

Благодаря предыдущему обучению в основном формируется архитектура использования Spring Cloud для реализации микросервисов, которая примерно выглядит следующим образом:

1525674644660

В этой архитектуре наш сервисный кластер включает в себя: внутренние сервисы Service A и Service B, которые регистрируются и подписываются на Eureka Server, в то время как Open Service — это внешний сервис, который доступен для вызывающих сервис через балансировку нагрузки. Мы фокусируемся на внешних сервисах и напрямую раскрываем адрес нашего сервиса. Является ли такая реализация разумной или есть лучший способ реализовать ее?

Во-первых, давайте поговорим о некоторых вещах, которые необходимо сделать, и о недостатках этой архитектуры:

  • Во-первых, он разрушает природу службы без сохранения состояния.
    • Чтобы обеспечить безопасность внешних сервисов, нам нужно реализовать контроль разрешений на доступ к сервису, а механизм контроля разрешений открытых сервисов будет работать и загрязнять бизнес-логику всего открытого сервиса.Самая непосредственная проблема, которую это принесет, это для уничтожения службы.. Безотказная природа REST API в кластерах.
    • С точки зрения конкретной разработки и тестирования, помимо собственно бизнес-логики, необходимо также учитывать контрольную обработку доступа к интерфейсу.
  • Во-вторых, существующие интерфейсы нельзя повторно использовать напрямую.
    • Когда нам нужно получить доступ к существующему интерфейсу в кластере, чтобы получить доступ к внешним службам, мы должны добавить логику проверки к исходному интерфейсу или добавить прокси-вызов для достижения контроля разрешений, и мы не можем напрямую повторно использовать исходный интерфейс.

Столкнувшись с проблемами, подобными описанным выше, как мы можем их решить? Ответ: сервисный шлюз!

Чтобы решить эти проблемы, нам нужно извлечь такие разрешения из нашего сервисного блока, и наиболее подходящей для этих логик является то, где фронтальный доступ, нам нужен более мощный сбалансированный загрузчик Сервисный шлюз.

Сервисный шлюз — неотъемлемая часть архитектуры микросервисов. В процессе предоставления REST API внешней системе через сервисный шлюз, помимо функций сервисной маршрутизации и балансировки нагрузки, он также имеет функции сервисной маршрутизации и балансировки нагрузки.权限控制и другие функции. Zuul в Spring Cloud Netflix играет такую ​​роль, обеспечивая переднюю защиту для микросервисной архитектуры и в то же время перенося более тяжелое, не связанное с бизнес-логикой, содержимое управления разрешениями на уровень маршрутизации служб, так что основная часть сервисного кластера может иметь более высокую возможность повторного использования и тестируемость.

9.1 Архитектура после добавления Zuul

1525675648881

  • Будь то запрос от клиента (ПК или мобильного), или внутренний звонок службы. Все запросы на услуги будут проходить через шлюз Zuul, а затем шлюз будет осуществлять аутентификацию, динамическую маршрутизацию и другие операции. Zuul — это единый вход наших услуг.

9.2 Знакомство с Зуулом

9.3. Быстрый старт

9.3.1. Новый проект

Заполните основную информацию:

1525675928548

Добавьте зависимости Zuul:

1525675991833

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 для проксирования службы пользовательского сервиса, сначала посмотрите на статус службы в панели управления:

1525676797879

  • 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 проксирует, он будет использовать ленту для доступа к балансировке нагрузки:

Журнал показывает, что используется балансировщик нагрузки:

1525677891119

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, которая наглядно показывает последовательность выполнения запроса в каждом фильтре.

1525681866862

  • Нормальный процесс:
    • При поступлении запроса он сначала проходит через претип фильтр, а затем маршрутизируется тип маршрутизации, запрос поступает к реальному поставщику услуг, выполняет запрос и после возврата результата доходит до постфильтра . Затем верните ответ.
  • Процесс исключения:
    • В течение всего процесса, если есть исключение в предварительном фильтре или фильтре маршрутизации, оно напрямую попадет в фильтр ошибок.После обработки ошибки запрос будет передан в фильтр POST и, наконец, возвращен пользователю.
    • Если у самого фильтра ошибок есть исключение, он в конечном итоге войдет в фильтр POST, а затем вернется.
    • Если в фильтре POST есть исключение, оно перейдет к фильтру ошибок, но когда оно отличается от предварительного и маршрутного, запрос больше не попадет в фильтр POST.

Список всех встроенных фильтров:

1525682427811

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