бред какой то
Той ночью у меня был подробный обмен аннотациями @FeignClient, круто!
В основном в технической группе я видел, как некоторые студенты задавали связанные вопросы, такие как: Что такое contextId? Будут ли несколько клиентов с одинаковым именем сообщать об ошибке?
Затем я чувствую необходимость написать статью, чтобы рассказать об использовании @FeignClient, выкроить время из своего плотного графика, написать статью непросто, не забудьте поставить лайк.
Формальный
Основное введение в Feign
Прежде всего, базовая популяризация, я боюсь, что некоторые студенты не были ознакомлены со Spring Cloud. Feign — это клиент REST с открытым исходным кодом от Netflix.Определив интерфейс и используя аннотации для описания информации об интерфейсе, вы можете инициировать вызов интерфейса.
Адрес гитхаба:GitHub.com/open feign/…
Ниже приведен базовый пример использования, приведенный на домашней странице GitHub.В этом примере Feign используется для вызова интерфейса GitHub.
interface GitHub {
@RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
@RequestLine("POST /repos/{owner}/{repo}/issues")
void createIssue(Issue issue, @Param("owner") String owner, @Param("repo") String repo);
}
public static class Contributor {
String login;
int contributions;
}
public static class Issue {
String title;
String body;
List<String> assignees;
int milestone;
List<String> labels;
}
public class MyApp {
public static void main(String... args) {
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
// Fetch and print a list of the contributors to this library.
List<Contributor> contributors = github.contributors("OpenFeign", "feign");
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}
}
}
Введение в Spring Cloud OpenFeign
Spring Cloud OpenFeign — это продукт команды Spring Cloud, объединяющий родной Feign в Spring Cloud. В приведенном выше примере использования нативного Feign все используемые аннотации встроены в Feign, но мы в основном основаны на аннотациях Spring MVC в разработке, которые не очень удобно вызывать. Таким образом, Spring Cloud OpenFeign расширяет поддержку аннотаций Spring MVC, а также интегрирует Ribbon и Eureka для обеспечения реализации HTTP-клиента с балансировкой нагрузки.
Адрес гитхаба:GitHub.com/spring - уродливо...
Официально предоставленные примеры использования:
@FeignClient("stores")
public interface StoreClient {
@RequestMapping(method = RequestMethod.GET, value = "/stores")
List<Store> getStores();
@RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
Store update(@PathVariable("storeId") Long storeId, Store store);
}
Введение в использование аннотаций FeignClient
value, name
Роль значения и имени одинакова. Если URL-адрес не настроен, настроенное значение будет использоваться в качестве имени службы для обнаружения служб. Вместо этого это просто имя.
serviceId
serviceId устарел, вы можете использовать name напрямую.
contextId
Например, у нас есть пользовательский сервис, но в пользовательском сервисе много интерфейсов, мы не хотим определять все вызывающие интерфейсы в одном классе, например:
Client 1
@FeignClient(name = "optimization-user")
public interface UserRemoteClient {
@GetMapping("/user/get")
public User getUser(@RequestParam("id") int id);
}
Client 2
@FeignClient(name = "optimization-user")
public interface UserRemoteClient2 {
@GetMapping("/user2/get")
public User getUser(@RequestParam("id") int id);
}
В этом случае запуск сообщит об ошибке, поскольку имя бина конфликтует, конкретная ошибка выглядит следующим образом:
Description:
The bean 'optimization-user.FeignClientSpecification', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
Решением может быть добавление следующей конфигурации, роль которой состоит в том, чтобы разрешить BeanDefinition с тем же beanName.
spring.main.allow-bean-definition-overriding=true
Другое решение — вручную указать разные contextId для каждого клиента, чтобы не было конфликтов.
Решение после конфликта имени бина приведено выше.Разберем роль contextId в Feign Client.При регистрации Feign Client Configuration требуется имя.Имя получается через метод getClientName:
String name = getClientName(attributes);
registerClientConfiguration(registry, name,
attributes.get("configuration"));
private String getClientName(Map<String, Object> client) {
if (client == null) {
return null;
}
String value = (String) client.get("contextId");
if (!StringUtils.hasText(value)) {
value = (String) client.get("value");
}
if (!StringUtils.hasText(value)) {
value = (String) client.get("name");
}
if (!StringUtils.hasText(value)) {
value = (String) client.get("serviceId");
}
if (StringUtils.hasText(value)) {
return value;
}
throw new IllegalStateException("Either 'name' or 'value' must be provided in @"
+ FeignClient.class.getSimpleName());
}
Видно, что если contextId сконфигурирован, будет использоваться contextId.Если конфигурации нет, за значением будет следовать имя и, наконец, serviceId. По умолчанию конфигурация отсутствует, и при наличии у службы нескольких клиентов Feign будет сообщено об ошибке.
Второй эффект заключается в том, что при регистрации FeignClient contextId будет использоваться как часть псевдонима клиента.Если квалификатор настроен, квалификатор будет использоваться в качестве псевдонима.
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
validate(attributes);
definition.addPropertyValue("url", getUrl(attributes));
definition.addPropertyValue("path", getPath(attributes));
String name = getName(attributes);
definition.addPropertyValue("name", name);
String contextId = getContextId(attributes);
definition.addPropertyValue("contextId", contextId);
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
// 拼接别名
String alias = contextId + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be
// null
beanDefinition.setPrimary(primary);
// 配置了qualifier优先用qualifier
String qualifier = getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
url
URL-адрес используется для настройки адреса указанной службы, что эквивалентно запросу этой службы напрямую, без выбора службы на ленте. Можно использовать такие сценарии, как отладка.
использовать список
@FeignClient(name = "optimization-user", url = "http://localhost:8085")
public interface UserRemoteClient {
@GetMapping("/user/get")
public User getUser(@RequestParam("id") int id);
}
decode404
Когда в запросе вызова возникает ошибка 404, значение decode404 равно true, тогда будет выполняться декодирование декодером, в противном случае будет выдано исключение.
Декодирование вернет вам фиксированный формат данных:
{"timestamp":"2020-01-05T09:18:13.154+0000","status":404,"error":"Not Found","message":"No message available","path":"/user/get11"}
Если выдается исключение, это информация об исключении.Если настроен откат, будет выполнена логика отката:
configuration
Конфигурация предназначена для настройки класса конфигурации Feign.В классе конфигурации вы можете настроить Feign Encoder, Decoder, LogLevel, Contract и т. д.
определение конфигурации
public class FeignConfiguration {
@Bean
public Logger.Level getLoggerLevel() {
return Logger.Level.FULL;
}
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("user", "password");
}
@Bean
public CustomRequestInterceptor customRequestInterceptor() {
return new CustomRequestInterceptor();
}
// Contract,feignDecoder,feignEncoder.....
}
использовать список
@FeignClient(value = "optimization-user", configuration = FeignConfiguration.class)
public interface UserRemoteClient {
@GetMapping("/user/get")
public User getUser(@RequestParam("id")int id);
}
fallback
Определите отказоустойчивый класс обработки, то есть резервную логику.Запасной класс должен реализовывать интерфейс Feign Client и не может знать информацию об исключении предохранителя.
резервное определение
@Component
public class UserRemoteClientFallback implements UserRemoteClient {
@Override
public User getUser(int id) {
return new User(0, "默认fallback");
}
}
использовать список
@FeignClient(value = "optimization-user", fallback = UserRemoteClientFallback.class)
public interface UserRemoteClient {
@GetMapping("/user/get")
public User getUser(@RequestParam("id")int id);
}
fallbackFactory
Это также отказоустойчивый процесс, и вы можете узнать ненормальную информацию о предохранителе.
резервное определение фабрики
@Component
public class UserRemoteClientFallbackFactory implements FallbackFactory<UserRemoteClient> {
private Logger logger = LoggerFactory.getLogger(UserRemoteClientFallbackFactory.class);
@Override
public UserRemoteClient create(Throwable cause) {
return new UserRemoteClient() {
@Override
public User getUser(int id) {
logger.error("UserRemoteClient.getUser异常", cause);
return new User(0, "默认");
}
};
}
}
использовать список
@FeignClient(value = "optimization-user", fallbackFactory = UserRemoteClientFallbackFactory.class)
public interface UserRemoteClient {
@GetMapping("/user/get")
public User getUser(@RequestParam("id")int id);
}
path
path определяет унифицированный префикс, когда FeignClient обращается к интерфейсу. Например, адрес интерфейса — /user/get.Если вы определяете префикс как user, то в пути к конкретному методу нужно написать только /get.
использовать список
@FeignClient(name = "optimization-user", path="user")
public interface UserRemoteClient {
@GetMapping("/get")
public User getUser(@RequestParam("id") int id);
}
primary
Первичный соответствует аннотации @Primary, которая по умолчанию имеет значение true.Для этой официальной настройки есть причина. Когда наш Feign реализует резервный вариант, это означает, что Feign Client имеет несколько идентичных bean-компонентов в контейнере Spring.Когда мы используем @Autowired для внедрения, мы не знаем, какой из них внедрить, поэтому нам нужно установить высокий приоритет Да, @ Первичная аннотация делает именно это.
qualifier
Квалификатор соответствует аннотации @Qualifier. Сценарий использования имеет очень слабую связь с основным выше. В общих сценариях вы можете напрямую внедрить @Autowired напрямую.
Если наш Feign Client имеет резервную реализацию, аннотация @FeignClient по умолчанию имеет значение primary=true, что означает, что у нас нет проблем с использованием @Autowired инъекции, и ваш Feign Client будет внедрен первым.
Если вы случайно установите для параметра primary значение false, будет сообщено об ошибке, когда вы вводите напрямую с помощью @Autowired, и вы не знаете, какой объект вводить.
Решение очевидное, вы можете установить для первичного значение true, если по какой-то особой причине вам нужно удалить настройку primary=true, как мы будем вводить в этом случае, мы можем настроить квалификатор, а затем использовать аннотацию @ The Qualifier вводится, как показано ниже:
Определение ложного клиента
@FeignClient(name = "optimization-user", path="user", qualifier="userRemoteClient")
public interface UserRemoteClient {
@GetMapping("/get")
public User getUser(@RequestParam("id") int id);
}
Внедрение фиктивного клиента
@Autowired
@Qualifier("userRemoteClient")
private UserRemoteClient userRemoteClient;
Если вам интересно, вы можете подписаться на мой публичный аккаунт в WeChat.обезьяний мир, читать больше технических статей в первый раз. У меня также есть открытый исходный код на моем GitHub.github.com/yinjihuan