Привет всем, я Санью~~
Давным-давно я написал две статьи об архитектурных принципах OpenFeign и Ribbon, двух основных компонентах Spring Cloud.
Но, честно говоря, с моей нынешней точки зрения, структуру и содержание этих двух статей можно улучшить.
Так уж получилось, что недавно я запланировал буклет об архитектурных принципах каждого компонента SpringCloud.
Поэтому я воспользуюсь этой возможностью и перепишу эти две статьи, чтобы восполнить недостатки предыдущих статей.
В этой статье сначала поговорим об основных архитектурных принципах OpenFeign.
Вся статья условно разделена на следующие четыре части:
Первая часть, отделенная от Spring Cloud, как выглядит оригинальный Feign?
Часть 2. Каковы основные компоненты Feign и как выглядит вся ссылка выполнения?
Часть 3. Как SpringCloud интегрирует Feign в свою собственную экосистему?
Часть 4. OpenFeign имеет несколько методов настройки. Каковы приоритеты различных методов настройки?
Хорошо, без лишних слов, давайте перейдем непосредственно к теме, чтобы изучить основные принципы архитектуры OpenFeign.
В повседневной разработке использовать Feign очень просто, всего три шага.
Шаг 1. Введение зависимостей
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
Шаг 2:В класс начальной загрузки добавьте@EnableFeignClients
аннотация
@SpringBootApplication
@EnableFeignClients
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Шаг 3. Напишите интерфейс FeignClient.
@FeignClient(name = "order")
@RequestMapping("/order")
public interface OrderApiClient {
@GetMapping
Order queryOrder(@RequestParam("orderId") Long orderId);
}
Позже, когда мы захотим использовать,Просто нужно сделать инъекциюOrderApiClient
объект на нем
Хотя его легко использовать, это не оригинальный способ использования Feign, а способ использования Feign после его интеграции в SpringCloud.
Первоначально исходный код Feign был открыт Netflix.
Позже Spring Cloud инкапсулировал Feign и интегрировал его в свою собственную экосистему, что упростило использование Feign.
А также дал ему более продвинутое имя OpenFeign.
В следующей статье нет четкого различия между значениями Feign и OpenFeign. Вам просто нужно знать, что это значит.
У Feign есть собственный способ его использования, а также аналогичные аннотации, относящиеся к Spring MVC, как показано ниже:
public interface OrderApiClient {
@RequestLine("GET /order/{orderId}")
Order queryOrder(@Param("orderId") Long orderId);
}
OrderApiClientОбъекты необходимо передавать вручнуюFeign.builder()
создать
public class FeignDemo {
public static void main(String[] args) {
OrderApiClient orderApiClient = Feign.builder()
.target(OrderApiClient.class, "http://localhost:8088");
orderApiClient.queryOrder(9527L);
}
}
Я полагаю, что каждый, кто хоть немного знаком с Feign, знает, что нижний уровень Feign на самом деле основан на динамическом прокси-сервере JDK.
такFeign.builder()
Последняя конструкция — прокси-объект.
Когда Feign создает динамический прокси, он анализирует аннотации и параметры метода.
Для получения Http-запросов необходимы базовые параметры и соответствие между этими параметрами и параметрами метода.
Например, URL-адрес HTTP-запроса, тело запроса — параметр метода, заголовок запроса — параметр метода и т. д.
Позже, при построении Http-запроса, вы будете знать, какая часть данных в Http-запросе соответствует пути запроса и первому параметру метода.
При вызове метода динамического прокси Feign соберет проанализированные выше базовые параметры и входные параметры метода Http-запроса в Http-запрос.
Затем отправьте HTTP-запрос, получите ответ, а затем преобразуйте содержимое тела ответа в соответствующий тип в соответствии с типом содержимого ответа.
Это общий принцип Feign.
В течение всего процесса генерации и вызова динамического прокси Feign необходимо полагаться на некоторые основные компоненты Feign для координации.
Как показано на рисунке ниже, показаны некоторые основные компоненты Feign.
Доступ к этим основным компонентам можно получить черезFeign.builder()
Сделать замену
Поскольку компонентов много, здесь я выберу несколько важных и расскажу о них с вами.
Я говорил об этом раньше Когда Feign создает динамический прокси, он анализирует аннотации и параметры метода.,получатьHttpЗапрос требует основных параметров
Функция этого интерфейса контракта — проведение анализа.
Реализация Contract по умолчанию анализирует собственные аннотации Feign.
При анализе для каждого метода создается объект MethodMetadata.
MethodMetadata инкапсулирует основные параметры, необходимые для HTTP-запросов, и соответствие между этими параметрами и параметрами метода.
Когда SpringCloud интегрировал Feign, чтобы Feign распознавал аннотации Spring MVC, он реализовал сам интерфейс Contract.
По названию также можно сказать, что он фактически используется для кодирования.
Конкретная функция заключается в сериализации параметров метода, соответствующих телу запроса, в массив байтов.
Реализация кодировщика Feign по умолчанию поддерживает только типы параметров метода, соответствующие телу запроса, в виде строковых и байтовых массивов.
Если это другие типы,Например, тип параметра метода, соответствующий телу запроса, имеет видAddOrderRequest.class
тип,В это времяниктоюридическая параAddOrderRequest
сериализация объектов
Это затрудняет использование этого кодировщика по умолчанию.
С тех пор Spring реализовал интерфейс Encoder.
Объект типа параметра метода, соответствующий любому телу запроса, может быть сериализован в массив байтов.
Функция декодера прямо противоположна функции кодировщика.
Кодировщик сериализует параметры метода, соответствующие телу запроса, в массив байтов.
Декодер фактически десериализует тело ответа из потока байтов в объект типа возвращаемого значения метода.
По умолчанию Decoder аналогичен Encoder. Он поддерживает только десериализацию в массивы байтов или строки.
Поэтому Spring также реализует декодер для расширения его функциональности.
Поток байтов, соответствующий телу ответа, может быть десериализован в любой объект типа возвращаемого значения.
Из параметров и возвращаемых значений методов интерфейса видно, что на самом деле это тот компонент, который в конечном итоге используется динамическим прокси-объектом для выполнения Http-запросов.
Реализация по умолчанию осуществляется через HttpURLConnection, предоставляемый JDK.
В дополнение к этому значению по умолчанию Feign также предоставляет реализацию на основе HttpClient и OkHttp.
в проекте,Чтобы заменить реализацию по умолчанию,Просто введите соответствующие зависимости,ЗданиеFeign.builder()
Просто устанавливайте его время от времени
Среда SpringCloud будет автоматически настроена в соответствии с введенными зависимостями.
В дополнение к трем вышеперечисленным реализациям,Самое главное, конечно, то, что оно основано набалансировка нагрузкиРеализация
Ниже приведена основная реализация, используемая OpenFeign для интеграции ленты.
Этот Клиент получит информацию об экземпляре службы с ленты на основе имени службы, то есть IP-адреса и порта.
После этого экземпляру службы будет отправлен HTTP-запрос через IP-адрес и порт.
Я считаю, что каждый должен быть знаком с InvoctionHandler.
Для динамических агентов JDK необходимо реализовать InvoctionHandler для создания динамических агентов.
Реализация метода вызова InvoctionHandler является основной логикой динамического прокси.
InvocationHandlerFactory на самом деле является фабрикой, которая создает InvocationHandler.
Таким образом, вы можете догадаться, что InvokeHandler, созданный с помощью InvoctionHandlerFactory, должен быть основной логикой выполнения динамического прокси Feign.
Реализация InvoctionHandlerFactory по умолчанию следующая:
Эта реализация по умолчанию также используется по умолчанию в среде SpringCloud.
Итак, перейдём непосредственно к классу реализации FeignInvocationHandler из InfectionHandler.
Из реализации видно, что в дополнение к некоторым методам класса Object в конечном итоге будет вызван метод вызова метода MethodHandler, соответствующий этому методу.
Так что обратите внимание,этотMethodHandlerСразуИнкапсулирует основную логику симуляции выполнения Http-вызовов.,очень важно,Будет упомянуто позже
Хотя SpringCloud по умолчанию использует реализацию по умолчанию, в конечном итоге используется FeignInvocationHandler.
Но когда другие фреймворки интегрируют экосистему SpringCloud, чтобы адаптироваться к OpenFeign, они иногда сами реализуют InvoctionHandler.
Например, распространенные фреймворки предохранителей с ограничением тока Hystrix и Sentinel реализовали свой собственный InvokeHandler.
Таким образом, такие операции, как ограничение тока и понижение версии, могут выполняться до и после выполнения MethodHandler, то есть до и после вызова интерфейса Http.
RequestInterceptor на самом деле является интерфейсом перехвата перед отправкой запроса.
Через этот интерфейс содержимое HTTP-запроса изменяется перед отправкой HTTP-запроса.
Например, мы можем установить некоторые общедоступные параметры, необходимые интерфейсу, такие как токен аутентификации и так далее.
@Component
public class TokenRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
template.header("token", «значение токена»);
}
}
Это компонент повтора, реализация по умолчанию следующая.
По умолчанию максимальное количество повторов составляет 5 раз.
В SpringCloud приведенная выше реализация не используется, но используется следующая реализация
Поэтому Spring Cloud не будет повторять попытку по умолчанию.
В этом разделе в основном представлены семь основных компонентов Feign и соответствующая реализация расширения Spring.
Для вашего удобства я составил следующую таблицу
интерфейс | эффект | Симулировать реализацию по умолчанию | Весенняя реализация |
---|---|---|---|
Contract | Анализ аннотаций и параметров метода и сопоставление параметров HTTP-запроса с параметрами метода. | Contract.Default | SpringMvcContract |
Encoder | Сериализуйте параметры метода, соответствующие телу запроса, в массив байтов. | Encoder.Default | SpringEncoder |
Decoder | Десериализовать поток байтов тела ответа в объект типа возвращаемого значения метода. | Decoder.Default | SpringDecoder |
Client | Отправить HTTP-запрос | Client.Default | LoadBalancerFeignClient |
InvocationHandlerFactory | Фабрика InvokeHandler, логика динамического прокси-сервера | InvocationHandlerFactory.Default | никто |
RequestInterceptor | Прежде чем отправлять HTTP-запрос, перехватите и измените содержимое HTTP-запроса. | никто | никто |
Retryer | Повторить компонент | Retryer.Default | никто |
Помимо этих, есть и другие компоненты, которые здесь не упомянуты.
Например, уровень журнала Logger.Level, вывод журнала Logger, если вам интересно, вы можете проверить это самостоятельно.
В предыдущем разделе говорилось об основных компонентах Feign. В этом разделе мы поговорим об основном принципе работы Feign, который в основном разделен на две части:
Здесь я сначала возьму демонстрационный код оригинального использования Feign, описанного выше.
public class FeignDemo {
public static void main(String[] args) {
OrderApiClient orderApiClient = Feign.builder()
.target(OrderApiClient.class, "http://localhost:8088");
orderApiClient.queryOrder(9527L);
}
}
Это видно из демо,наконец прошлоFeign.builder().target(xx)
получатьприезжатьдинамический проксииз
Логика выполнения приведенного выше кода следующая:
Наконец, будет вызван метод newInstance ReflectiveFeign для создания динамического прокси-объекта.
ReflectiveFeign внутренне настраивает некоторые из основных компонентов, упомянутых ранее.
Далее давайте посмотрим на метод newInstance.
Этот метод в основном делает две вещи:
Первое, что нужно сделать, это проанализировать интерфейс и построить обработчик метода, соответствующий каждому методу.
MethodHandler особенно напомнил мне, когда я ранее говорил о InvoctionHandlerFactory.
Динамический прокси (FeignInvoctionHandler) в конечном итоге вызовет MethodHandler для обработки Http-вызова от Feign.
При разборе интерфейса,Ранее упомянутый контракт будет использоваться для анализа параметров и аннотаций метода.,Генерироватьметодметаданные,Я не буду публиковать здесь свой код.
Во-вторых, необходимо создать InvoctionHandler через InvoctionHandlerFactory.
Затем создайте активный прокси-объект интерфейса.
ок, теперь мы фактически завершили процесс генерации динамического прокси.
такдинамический Логика генерации прокси очень проста. Всего несколько строк кода. Нарисуйте картинку. итог
Я сказал это раньше,При вызове интерфейсдинамического прокси,проходитьInvoctionHandler(FeignInvoctionHandler),Наконец, он передается на выполнение методу вызова MethodHandler.
MethodHandler — это интерфейс, который в конечном итоге достигает реализации метода вызова своего класса реализации SynchronousMethodHandler.
Свойства в SynchronousMethodHandler — это некоторые из компонентов, о которых мы упоминали ранее.
Поскольку вся ссылка на выполнение вызова кода относительно длинная, я не буду здесь обрезать код. Если вам интересно, вы можете прочитать его самостоятельно.
Но здесь я нарисовал картинку, по которой можно примерно проанализировать весь процесс вызова Feign Http.
Это процесс выполнения Http-вызова Feign.
Если есть настройка повтора, она также вступит в силу на этом этапе.
Когда SpringCloud интегрирует Feign, он в основном делится на две части.
Первая часть перереализации основных компонентов уже упоминалась ранее, поэтому я не буду повторять ее здесь.
Что касается второй части, давайте поговорим о том, как Spring использует интерфейсдинамический. прокси-объект вводится в контейнер Springиз
При использовании OpenFeign,необходимо добавить@EnableFeignClients
Эта аннотация является движком OpenFeign.
@EnableFeignClients
наконецпроходить@Import
аннотация ИмпортированFeignClientsRegistrar
FeignClientsRegistrar
ОсуществленныйImportBeanDefinitionRegistrar
такфинальныйSpringпри запускеизкогдапозвонюregisterBeanDefinitions
метод实现
之такпозвоню
registerBeanDefinitions
метод,да@Import
аннотацияизэффект,Не уверениз Студенты могут посмотретьВзгляните на позы внедрения бобов в Spring. Сколько вы знаете?
финальныйвстреча走приезжатьregisterFeignClients
этотметод
Хотя этот метод относительно длинный, в основном он выполняет следующие две задачи:
первое дело,сканирование@EnableFeignClients
расположениедобрыйиз Пакеты и подпакеты(Если есть указанный пакет Сразу Сканируйте указанный пакет),Найти все добавленные@FeignClient
аннотацияизинтерфейс,Сгенерируйте кучу BeanDefinitions
Этот BeanDefinition содержит такую информацию, как этот интерфейс
Второе — зарегистрировать эти отсканированные интерфейсы в контейнере Spring.
При регистрации,Не тип интерфейса прямой регистрации,идаFeignClientFactoryBean
тип
хорошо,ко всему этому@EnableFeignClients
Процесс запуска Сразувсе кончено
Хотя это очень долго,нодавесь@EnableFeignClients
На самом деле Сразу Сделал только одну основную вещьизслучай
сканированиеприезжать所有издобавлять@FeignClient
аннотацияизинтерфейс
а затем длякаждыйинтерфейсгенерироватьодинBeanтипдляFeignClientFactoryBean
изBeanDefinition
Наконец зарегистрирован в контейнере Spring
Упоминалось в предыдущем разделе,каждыйинтерфейс Все соответствуют одномуclassтипдляFeignClientFactoryBean
изBeanDefinition
как показано выше,FeignClientFactoryBean
даодинFactoryBean
иFeignClientFactoryBean
из Эти свойства,Он устанавливается при создании BeanDefinition.
И этот атрибут типа представляет собой тип интерфейса.
Благодаря внедрению FactoryBean,Итак, во время запуска Spring,一定дляпозвонюgetObject
методполучатьнастоящийизBeanобъект
Я не буду говорить об эффекте FactoryBean.,Не уверениз Маленький Партнер такжеда Вы можете взглянутьВзгляните на позы внедрения бобов в Spring. Сколько вы знаете?Эта статья
getObject
финальныйвстреча走приезжатьgetTarget()
метод
Из приведенного выше кода видно, что,финальныйвозвращатьсядавстречапроходитьFeign.builder()
создатьдинамический проксиобъект
Единственное отличие состоит в том, что SpringCloud заменит стандартные компоненты Feign и реализует их самостоятельно.
В общем, Spring добавляет динамические прокси-объекты Feign в контейнер Spring через FactoryBean.
Поскольку основные компоненты Feign можно заменить, как нам настроить наши собственные компоненты в среде Spring Cloud?
Но прежде чем говорить о конфигурации, давайте поговорим об операции изоляции конфигурации FeignClient.
В среде Spring Cloud, чтобы изолировать разные клиенты Feign друг от друга.
Когда приложение запускается,Для каждого интерфейса FeignClient будет создан контейнер Spring.,Далее я назову этот контейнер контейнером FeignClient.
Эти контейнеры FeignClient Spring имеют один и тот же родительский контейнер, который является контейнером, созданным при запуске проекта.
SpringCloudдамкаждыйFeignClientконтейнер添加один默认из КонфигурациядобрыйFeignClientsConfiguration
Конфигурациядобрый
Этот класс конфигурации объявляет различные компоненты Feign.
Поэтому по умолчанию OpenFeign использует эти настроенные компоненты для создания прокси-объектов.
Зная, как настроить изоляцию, давайте рассмотрим конкретные методы настройки и соотношение приоритетов между ними.
Приведите пример,Например, я объявляю один вручнуюContract
объект,типдляMyContract
public class FeignConfiguration {
@Bean
public Contract contract(){
return new MyContract();
}
}
Внимание Внимание,здесь
FeignConfiguration
я не добавлял это@Configuration
аннотация,Причина будет обсуждаться позже
Конфигурация на данный момент следующая:
@EnableFeignClients(defaultConfiguration = FeignConfiguration.class)
послеэтот Конфигурациядобрыйвстреча被加приезжатькаждыйFeignClientконтейнерсередина,такэтот Конфигурация Это эффективно для всех FeignClients.
И приоритет выше, чем приоритет, настроенный по умолчанию.
напримерэтотпример Сразувстреча使得FeignClientиспользуйте оператор iизMyContract
,и不даFeignClientsConfiguration
серединазаявлениеизSpringMvcContract
вернуть услугувышеизFeignConfiguration
Конфигурациядобрый Пример,Можетпроходить@FeignClient
аннотация Конфигурация
@FeignClient(name = "order", configuration = FeignConfiguration.class)
в это времяэтот Конфигурациядобрыйвстреча被加приезжатьСобственный контейнер FeignClient.середина,Уведомлениеда Собственный контейнер FeignClient.
так Этот вид КонфигурацияизэффектобъемдаМой собственный FeignClient
иЭтот вид Конфигурацияизприоритетдабольше, чем@EnableFeignClients
аннотация Конфигурацияизприоритет
Как упоминалось ранее, поскольку родительским контейнером всех контейнеров FeignClient является контейнер, запущенный проектом
Таким образом, вы можете поместить конфигурацию в контейнер, запущенный этим проектом.
вернуть услугуFeignConfiguration
Например,плюс@Configuration
аннотация,Пусть контейнер, запущенный проектом, успешно просканируется.
Этот вид КонфигурацияизПриоритет выше, чем у всех ранее упомянутых приоритетов конфигурации.
иЭто эффективно для всех FeignClients.
так,этот Сразудадля什么使用аннотация Конфигурацияпочему Конфигурациядобрый Невозможно добавить
@Configuration
аннотацияизпричина,Потому что как только он сканируется контейнером, запущенным проектом,Эта конфигурация будет действовать для всех FeignClients.,И приоритет самый высокий,Это приведет к сбою другой конфигурации.,Конечно, вы также можете добавить@Configuration
аннотация,Но его не должен сканировать контейнер, запущенный проектом.
В дополнение к трем вышеупомянутым конфигурациям методов кодирования OpenFeign также поддерживает настройку через файлы конфигурации.
И он также поддерживает действие на все FeignClients и действие на один FeignClient одновременно.
Конфигурация действительна для всех FeignClients:
feign:
client:
config:
default: # default Представляет глобальный эффект
contract: com.sanyou.feign.MyContract
Эффективная конфигурация для одного FeignClient:
feign:
client:
config:
order: # конкретное название службы
contract: com.sanyou.feign.MyContract
По умолчанию этот метод файла конфигурации имеет наивысший приоритет.
нодаесли ты Конфигурациядокументсередина Воля Конфигурацияэлементfeign.client.default-to-properties
установлен наfalse
изразговаривать,Файл конфигурации имеет самый низкий приоритет.
feign:
client:
default-to-properties: false
В этом разделе Подвести итог представлены 4 способа OpenFeign, а также их приоритет и диапазон эффектов.
Нарисуй картинку итог
Если вы по-прежнему сталкиваетесь с некоторыми проблемами приоритета во время конкретного использования, вы можете отладить эту часть исходного кода, чтобы увидеть, какая конфигурация вступит в силу.
На этом этапе мы наконец закончили говорить об основных архитектурных принципах OpenFeign.
Это еще одна красноречивая статья на 10 000 слов.
Поскольку OpenFeign — это всего лишь фреймворк, здесь нет сложного механизма.
Таким образом, вся статья больше сосредоточена на исходном коде.
Я не знаю, как ты выглядишь и чувствуешь
Если вам хорошо, ставьте лайк, смотрите, собирайте и делитесь с теми, кому это нужно.
Ваша поддержка — моя самая большая мотивация для обновлений, спасибо!
Дополнительные статьи серии SpringCloud можно просмотреть в строке меню официального аккаунта.
Ладно, на этом статья закончена, увидимся в следующий раз, пока!