在上篇文章我们看了服务注册与发现Eureka,今天我们看下服务之间的调用组件。
各组件深入之Spring Cloud openFeign
在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于HTTP RESTful的。Spring Cloud有两种服务调用方式,一种是Ribbon+RestTemplate,另一种是Feign。
Feign是声明性Web服务客户端。 它使编写Web服务客户端更加容易。 要使用Feign,请创建一个接口并对其进行注释。 它具有可插入注释支持,包括Feign注释和JAX-RS注释。 Feign还支持可插拔编码器和解码器。 Spring Cloud添加了对Spring MVC注释的支持,并支持使用Spring Web中默认使用的相同HttpMessageConverters。 Spring Cloud集成了Eureka和Spring Cloud LoadBalancer,以在使用Feign时提供负载平衡的http客户端。 就是通过把http请求封装到了注解中。
Spring Cloud 的 Feign 支持中的一个核心概念是命名客户机。每个佯装的客户机都是一个组件集合的一部分,这些组件一起工作,根据需要联系一个远程服务器,这个集合有一个名称,作为一个使用@feignclient 注释的应用程序开发人员,你可以给它一个名称。Spring Cloud 使用 FeignClientsConfiguration 根据需要为每个命名客户机创建一个新的集合,作为 ApplicationContext。其中包括一个假动作。解码器,一个假装。编码器,和一个假装。合约。可以使用@feignclient 注释的 contextId 属性覆盖集合的名称。
Hystrix 支持熔断(fallback)的概念: 一个默认的代码路径,在熔断或出现错误时执行。要为给定的@feignclient 启用熔断,请将熔断属性设置为实现熔断的类名。您还需要将实现声明为 springbean。
/**
* 去请求feign服务端itoken-service-admin中的服务接口
* @Author kay三石
* @date:2019/6/22
*/
// value 是声明的方式指向了 服务提供者
@FeignClient(value="itoken-service-admin",fallback = AdminServiceFallback.class)
public interface AdminService extends BaseClientService {
/**
* 根据 ID 获取管理员
*
* @return
*/
@RequestMapping(value = "v1/admins", method = RequestMethod.GET)
public String get(
@RequestParam(required = true, value = "userCode") String userCode
);
}
如果需要访问制造回退触发器的原因,可以在@feignclient 中使用 fallbackFactory 属性。
// name 调用服务的名称和value等
@FeignClient(name=ServiceNameConstants.DEMOB_SERVICE,fallbackFactory = DemobServiceClientFallbackFactory.class)
public interface DemobServiceClient {
@GetMapping(value = "/demob/test/getDemobById")
DemobDTO getDemobById(@RequestParam("id")String id);
}
@Component
public class DemobServiceClientFallbackFactory implements FallbackFactory<DemobServiceClient> {
@Override
public DemobServiceClient create(Throwable cause) {
DemobServiceClientFallback demobServiceClientFallback = new DemobServiceClientFallback();
demobServiceClientFallback.setCause(cause);
return demobServiceClientFallback;
}
}
@Slf4j
public class DemobServiceClientFallback implements DemobServiceClient {
@Setter
private Throwable cause;
@Override
public DemobDTO getDemobById(String id) {
log.error("根据id获取demob信息失败",cause);
throw new FirstException();
}
}
注解 | 接口Target | 使用说明 |
---|---|---|
@RequestLine |
方法上 | 定义HttpMethod 和 UriTemplate. UriTemplate 中使用{} 包裹的表达式,可以通过在方法参数上使用@Param 自动注入 |
@Param |
方法参数 | 定义模板变量,模板变量的值可以使用名称的方式使用模板注入解析 |
@Headers |
类上或者方法上 | 定义头部模板变量,使用@Param 注解提供参数值的注入。如果该注解添加在接口类上,则所有的请求都会携带对应的Header信息;如果在方法上,则只会添加到对应的方法请求上 |
@QueryMap |
方法上 | 定义一个键值对或者 pojo,参数值将会被转换成URL上的 query 字符串上 |
@HeaderMap |
方法上 | 定义一个HeaderMap, 与 UrlTemplate 和HeaderTemplate 类型,可以使用@Param 注解提供参数值 |
FeignClient 的配置参数
属性名 | 默认值 | 作用 | 备注 |
---|---|---|---|
value | 空字符串 | 调用服务名称,和name属性相同 | |
serviceId | 空字符串 | 服务id,作用和name属性相同 | 已过期 |
name | 空字符串 | 调用服务名称,和value属性相同 | |
url | 空字符串 | 全路径地址或hostname,http或https可选 | |
decode404 | false | 配置响应状态码为404时是否应该抛出FeignExceptions | |
configuration | {} | 自定义当前feign client的一些配置 | 参考FeignClientsConfiguration |
fallback | void.class | 熔断机制,调用失败时,走的一些回退方法,可以用来抛出异常或给出默认返回数据。 | 底层依赖hystrix,启动类要加上@EnableHystrix |
path | 空字符串 | 自动给所有方法的requestMapping前加上前缀,类似与controller类上的requestMapping | |
primary | true |
Feign原理:
- 启动时,程序会进行包扫描,扫描所有包下所有@FeignClient注解的类,并将这些类注入到spring的IOC容器中。当定义的Feign中的接口被调用时,通过JDK的动态代理来生成RequestTemplate。
- RequestTemplate中包含请求的所有信息,如请求参数,请求URL等。
- RequestTemplate声场Request,然后将Request交给client处理,这个client默认是JDK的HTTPUrlConnection,也可以是OKhttp、Apache的HTTPClient等。
- 最后client封装成LoadBaLanceClient,结合ribbon负载均衡地发起调用。
@FeignClient
/**
* The name of the service with optional protocol prefix. Synonym for {@link #name()
* name}. A name must be specified for all clients, whether or not a url is provided.
* Can be specified as property key, eg: ${propertyKey}.
*/
@AliasFor("name")
String value() default "";
/**
* The service id with optional protocol prefix. Synonym for {@link #value() value}.
*
* @deprecated use {@link #name() name} instead
*/
@Deprecated
String serviceId() default "";
/**
* The service id with optional protocol prefix. Synonym for {@link #value() value}.
*/
@AliasFor("value")
String name() default "";
/**
* Sets the <code>@Qualifier</code> value for the feign client.
*/
String qualifier() default "";
/**
* An absolute URL or resolvable hostname (the protocol is optional).
*/
String url() default "";
/**
* Whether 404s should be decoded instead of throwing FeignExceptions
*/
boolean decode404() default false;
/**
* A custom <code>@Configuration</code> for the feign client. Can contain override
* <code>@Bean</code> definition for the pieces that make up the client, for instance
* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
*
* @see FeignClientsConfiguration for the defaults
*/
Class<?>[] configuration() default {};
/**
* Fallback class for the specified Feign client interface. The fallback class must
* implement the interface annotated by this annotation and be a valid spring bean.
*/
Class<?> fallback() default void.class;
/**
* Define a fallback factory for the specified Feign client interface. The fallback
* factory must produce instances of fallback classes that implement the interface
* annotated by {@link FeignClient}. The fallback factory must be a valid spring
* bean.
*
* @see feign.hystrix.FallbackFactory for details.
*/
Class<?> fallbackFactory() default void.class;
/**
* Path prefix to be used by all method-level mappings. Can be used with or without
* <code>@RibbonClient</code>.
*/
String path() default "";
/**
* Whether to mark the feign proxy as a primary bean. Defaults to true.
*/
boolean primary() default true;
EnableFeignClients 才是真正的boss
所有的逻辑都是在FeignClientsRegister这个类中进行的。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
/**
* Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
* declarations e.g.: {@code @ComponentScan("org.my.pkg")} instead of
* {@code @ComponentScan(basePackages="org.my.pkg")}.
* @return the array of 'basePackages'.
*/
String[] value() default {};
/**
* Base packages to scan for annotated components.
* <p>
* {@link #value()} is an alias for (and mutually exclusive with) this attribute.
* <p>
* Use {@link #basePackageClasses()} for a type-safe alternative to String-based
* package names.
*
* @return the array of 'basePackages'.
*/
String[] basePackages() default {};
/**
* Type-safe alternative to {@link #basePackages()} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
*
* @return the array of 'basePackageClasses'.
*/
Class<?>[] basePackageClasses() default {};
/**
* A custom <code>@Configuration</code> for all feign clients. Can contain override
* <code>@Bean</code> definition for the pieces that make up the client, for instance
* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
*
* @see FeignClientsConfiguration for the defaults
*/
Class<?>[] defaultConfiguration() default {};
/**
* List of classes annotated with @FeignClient. If not empty, disables classpath scanning.
* @return
*/
Class<?>[] clients() default {};
}
详细的EnableFeignClients 源码解析可以参考
好了这就是我对fegin的了解。