OpenFegin微服务调用:
1、OpenFegin 介绍
1.1 Fegin
Feign是SpringCloud组件中的一个轻量级HTTP服务客户端,
Feign内置了Ribbon,用来做客户端负载均衡。
Feign使用注解定义接口,实现服务间通信,
Feign本身不支持Spring MVC的注解,它有一套自己的注解。
1.2 OpenFegin
OpenFeign是Spring Cloud 在Feign的基础上支持了Spring MVC的注解
通过动态代理的方式实现服务间通信,并内置Ribbon实现客户端负载均衡的能力。
2、OpenFegin使用
2.1 第一步:依赖<br /> <br /> <dependency><br /> <groupId>org.springframework.cloud</groupId><br /> <artifactId>spring-cloud-starter-openfeign</artifactId><br /> </dependency><br /> <br /> 2.2 第二步:在启动类上开启Fegin客户端支持<br /> <br /> @SpringBootApplication<br /> @EnableFeignClients<br /> public class PortalApplication {<br /> <br /> }<br /> <br /> 2.3 第三步:编写fegin接口<br /> <br /> 注意:fegin的接口,与服务提供者controller方法签名一致<br /> <br /> /**<br /> * @FeignClient<br /> * 1、表示fegin的客户端接口,用来实现远程调用的。<br /> * 2、value 指定远程调用的服务名称<br /> */<br /> @FeignClient(value = "cinema-member-service")<br /> public interface MemberFeginClient {<br /> @GetMapping("/member/{id}")<br /> String findMemberById(@PathVariable("id") Integer id);<br /> }<br /> <br /> 2.4 第四步:在controller中注入fegin客户端接口对象,进行远程调用<br /> <br /> @RestController<br /> @RequestMapping("/fegin")<br /> public class PortalFeginController {
@Autowired<br /> private MemberFeginClient memberFeginClient;
@GetMapping("/{id}")<br /> public String findMemberById(@PathVariable("id")Integer id){
// 通过fegin的客户端进行远程调用<br /> String result = memberFeginClient.findMemberById(id);
return "远程调用返回结果:" + result;<br /> }<br /> }<br /> <br /> 2.5 访问<br /> <br /> [http://localhost:8888/fegin/100](http://localhost:8888/fegin/100)<br /> <br /> <br /> <br /> <br />3、OpenFegin 集成Ribbon
3.1 默认集成Ribbon, 查看间接依赖<br /> <br /> 3.2 负载均衡局部配置<br /> cinema-member-service: # 服务名称<br /> ribbon:<br /> NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule<br /> <br /> 3.3 负载均衡全局配置<br /> @Bean<br /> public IRule rules(){<br /> return new RandomRule();<br /> }<br /> <br /> <br />4、OpenFeign开启通信日志
4.1 需要查看远程调用更完成的日志信息,需要开启通讯日志配置。<br /> <br /> 4.2 配置实现<br /> <br /> 第一步:配置Logger类<br /> <br /> // 开启fegin远程调用通讯日志<br /> @Bean<br /> public Logger.Level loggerLevel(){<br /> return Logger.Level.FULL;<br /> }<br /> <br /> 第二步:配置日志输出级别<br /> <br /> logging:<br /> level:<br /> com.wnxy: debug<br /> <br /> <br /> 第三步:远程调用查看日志<br />
5、OpenFegin 超时重试
5.1 服务提供者提供超时方法<br /> <br /> cinema-member-service:<br /> <br /> @GetMapping("timeout")<br /> public String findTimeout() {<br /> log.info("访问超时方法!");<br /> try {<br /> TimeUnit.SECONDS.sleep(3);<br /> } catch (InterruptedException e) {<br /> e.printStackTrace();<br /> }<br /> return "访问超时方法!";<br /> }<br /> <br /> 5.2 服务消费者:<br /> <br /> cinema-portal-service:<br /> <br /> A. fegin接口添加方法<br /> <br /> @FeignClient(value = "cinema-member-service")<br /> @RequestMapping("/member")<br /> public interface MemberFeginClient {<br /> @GetMapping("/{id}")<br /> String findMemberById(@PathVariable("id") Integer id);
@GetMapping("/timeout")<br /> String findTimeout();<br /> }<br /> <br /> B. yml超时配置<br /> <br /> feign:<br /> client:<br /> config:<br /> default:<br /> # 连接服务提供者的超时时间<br /> connectTimeout: 1000<br /> # 处理请求的超时时间<br /> readTimeout: 2000<br /> <br /> C. 超时重试bean配置<br /> <br /> // 超时重试<br /> @Bean<br /> public Retryer feignRetryer() {<br /> return new Retryer.Default();<br /> }<br /> <br />
6、Openfegin 远程调用
6.1 服务提供者cinema-member-service提供UserController方法,里面有业务方法<br /> <br /> 6.2 服务消费者cinema-portal-service<br /> <br /> A. 编写UserFeginClient<br /> <br /> B. 编写UserFeginController,注入UserFeginClient,通过 Proxy 代理实现远程调用。<br /> <br /> 6.3 注意<br /> <br /> i. 在微服务的通讯调用,可以通过openfegin实现远程调用。 比RestTeplate更方便。<br /> <br /> ii. 如果一个服务对应多个fegin接口,需要配置spring.main.allow-bean-definition-overriding=true<br /> <br /> iii. 方法签名要与服务提供者接口一致; @PathVariable("id")中的id不能省略。<br /> <br /> @GetMapping("/{id}")<br /> User findOne(@PathVariable("id") Integer id) ;<br />
7、Openfegin源码分析
7.1 问题:<br /> 在cinema-portal-service微服务中,编写fegin接口,就可以远程调用cinema-member-service,why?<br /> <br /> 【原理:Java动态代理】<br /> <br /> Java动态代理:<br /> A. Jdk动态代理; <br /> <br /> 1. 接口的动态代理, 要求目标对象一定要实现接口<br /> <br /> Proxy.newProxyInstance(ClassLoader loader, 类加载器<br /> Class<?>[] interfaces, 目标对象实现的接口<br /> InvocationHandler h) 事件处理器,当执行代理方法时候会触发这里<br /> <br /> 2. 在运行时期,在内存中动态构建字节码对象<br /> <br /> B. Cglib动态代理<br /> <br /> 1. 子类代理,基于asm字节码处理框架实现;spring2以后的版本已经集成了cglib包。<br /> <br /> 2. 原理:在运行时期对目标对象生成子类的方式实现代理<br /> <br /> 目标对象:class Person{}<br /> <br /> 代理对象:class PersonProxy extends Person {}<br /> <br /> 3. 使用Api<br /> Enhancer enhancer = new Enhancer();<br /> enhancer.setSuperclass();<br /> enhancer.create() // 创建代理<br /> <br /> 4. 注意<br /> 4.1 目标对象不能为final,否则报错. 原因:生成不了子类<br /> <br /> 4.2 目标对象对象方法可以为final/static,但是不会被代理拦截增强。<br /> <br /> 7.2 源码分析<br /> <br /> A. 首先,通过@EnableFeignClients开启Openfegin的使用,Spring框架会检查此注解,如果存在则加载fegin相关配置信息<br /> <br /> B. 进入注解中,看看<br /> <br /> @SpringBootApplication<br /> @EnableFeignClients<br /> public class PortalApplication { }<br /> <br /> <br /> @Import(FeignClientsRegistrar.class)<br /> public @interface EnableFeignClients {}<br /> <br /> 进入FeignClientsRegistrar:<br /> public void registerBeanDefinitions(AnnotationMetadata metadata, // 注册BeanDefinition,也就是往ioc容器中注入代理对象<br /> BeanDefinitionRegistry registry) {<br /> registerDefaultConfiguration(metadata, registry);<br /> registerFeignClients(metadata, registry); // 注册fegin客户端对象到容器<br /> }<br /> <br /> C. 扫描@FeginClient注解修饰的类,会把类的内容解析为BeanDefinition<br /> <br /> D. 通过FeignClientFactoryBean这个工厂完成bean代理对象的创建<br /> <br /> 会调用其中的getObject()方法,最终执行在ReflectiveFeign.newInstance()方法中执行<br /> <br /> InvocationHandler handler = factory.create(target, methodToHandler);<br /> T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),<br /> new Class<?>[] {target.type()}, handler);<br /> <br /> 创建代理对象!<br /> <br /> 后续当执行代理方法时候【userFeginClient.findOne(id)】, 由feigin的代理的事件处理程序handler负责http远程调用以及结果的解析<br /> <br /> E. 最终,通过调用Spring框架的<br /> <br /> BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry)<br /> <br /> 讲解析处理过的FeginClient 修饰的接口的代理 (BeanDefinition) 加入到ioc容器中<br /> <br /> <br />8、小结
Openfegin调用执行原理:<br /> <br /> 1、通过@EnableFeignClients开启Openfegin的使用,<br /> <br /> Spring框架会检查此注解,如果存在则加载fegin相关配置信息,并扫描类路径中@FeginClient修饰的类。<br /> <br /> 2、解析@FeginClient修饰的类,fegin框架会把解析的结果封装为BeanDefinition的定义<br /> <br /> 3、通过FeignClientFactoryBean其中的getObject()方法最终通过jdk动态代理生成代理对象】<br /> <br /> 4、把生成的代理对象加入ioc容器,BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry)<br /> <br /> 5、当执行代理方法时候,针对proxy代理的调用,会把他交给fegin框架中的 代理的 handler处理器 统一处理,<br /> <br /> 最终完成http请求发送,接收响应的处理过程。