我们在上一节学习了eureka。让我们回顾一下。首先是cs架构,分为client和server。客户端也分为生成器和消费者,即服务提供者和服务消费者,具体客户端的作用如下。当客户端启动时,它向服务器注册当前服务并与服务器保持心跳。它使用后台线程拉取服务器上的节点集合,然后定期更新服务。信息发送到本地,因为客户端也缓存了服务节点信息。当服务挂掉时,它会监听shutdown,并向服务器报告自己的挂断状态。服务器启动后,从其他节点获取服务注册信息。在运行过程中,定期运行evict任务,剔除未按时更新的服务(包括非正常停止和网络故障的服务)。在运行过程中,接收到的注册、更新、取消请求会同步到其他注册中心节点。分布式数据同步(AP)运行过程中,存在自我保护机制。等等SpringCloud的eureka原理是什么?FeignFeign是一个声明式、模板化的HTTP客户端(仅在ApplicationClient中使用)。声明式调用是指像调用本地方法一样调用远程方法,不需要感知和操作远程HTTP请求。SpringCloud的声明式调用可以达到使用HTTP请求远程服务时调用本地方法一样的体验。开发人员完全不知道这是一个远程方法,更不用说HTTP请求了。Feign的应用像Dubbo一样进行SpringCloud微服务调用。ApplicationClient直接通过接口方法调用ApplicationService,不需要通过常规的RestTemplate构造请求然后解析返回数据。它解决了让开发者像调用本地方法一样调用远程接口的问题,而不必关注与远程交互的细节,更不用说分布式环境的开发了。Feign是一个声明式的Web服务客户端。它使编写Web服务客户端变得更加容易。要使用Feign,请创建一个接口并对其进行注释。它具有可插入的注释支持,包括Feign注释和JAX-RS注释。Feign还支持可插入的编码器和解码器。SpringCloud增加了对SpringMVC注解的支持,支持使用SpringWeb默认使用的HttpMessageConverters注解。在使用Feign时,SpringCloud集成了Ribbon和Eureka来提供一个负载均衡的http客户端。使用Feign开发时应用部署结构Feign是如何设计的?虽然我们在SpringCloud全家桶中更多的是使用原生的Feign,但是实际上呢?他只是给原生的fegin做了一些封装,所以想要一探究竟,还是需要多了解一下。原生的Fegin对我们理解SpringCloudfeign很有帮助。Feign的基本用法如下。规范Retrofit样本的改编。interfaceGitHub{//RequestLine注解声明请求方法和请求地址,可以允许查询参数@RequestLine("GET/repos/{owner}/{repo}/contributors")Listcontributors(@Param("owner")Stringowner,@Param("repo")Stringrepo);}staticclassContributor{Stringlogin;intcontributions;}publicstaticvoidmain(String...args){GitHubgithub=Feign.builder().decoder(newGsonDecoder()).target(GitHub.class,"https://api.github.com");//Fetchandprintalistofthecontributorstothislibrary.Listcontributors=github.contributors("OpenFeign","feign");for(Contributorcontributor:contributors){System.out.println(contributor.login+"("+contributor.contributions+")");}}自定义Feign有很多方面可以自定义。举个简单的例子,你可以使用Feign.builder()来构造一个API接口,你自己的组件。如下:interfaceBank{@RequestLine("POST/account/{id}")AccountgetAccountInfo(@Param("id")Stringid);}...//AccountDecoder()是一个DecoderBankbank=Feign.builder()实现的自己.decoder(newAccountDecoder()).target(Bank.class,https://api.examplebank.com);Feign动态代理Feign默认实现是ReflectiveFeign,通过Feign.Builder构建。在查看代码之前,让我们了解一下Target对象。publicinterfaceTarget{//接口的类型Classtype();//代理对象的名称,默认为url,Stringname()用于负载均衡;//请求的url地址,eg:https://api/v2Stringurl();}其中,Target.type用于生成代理对象,url为Client对象发送请求的地址。publicFeignbuild(){//client有三种实现JdkHttp/ApacheHttp/okHttp,默认是jdk的实现SynchronousMethodHandler.FactorysynchronousMethodHandlerFactory=newSynchronousMethodHandler.Factory(client,retryer,requestInterceptors,logger,logLevel,decode404,closeAfterDecode,propagationPolicy);ParseHandlersByNamehandlersByName=newParseHandlersByName(合同、选项、编码器、解码器、queryMapEncoder、errorDecoder、synchronousMethodHandlerFactory);returnnewReflectiveFeign(handlersByName,invocationHandlerFactory,queryMapEncoder);}总结:先介绍几个主要的参数:Client这个没什么好说的,一共有三种实现JdkHttp/ApacheHttp/okHtRequestInterceptor请求拦截器ContractREST注解解析器,默认是Contract.Default(),支持Feign的原生注解。InvocationHandlerFactory生成JDK动态代理,实际执行委托给MethodHandler。生成一个代理对象publicTnewInstance(Targettarget){//1.Contract将target.type接口类上的方法和注解解析成MethodMetadata,//转换成MethodHandler内部处理方法Map<字符串,MethodHandler>nameToHandler=targetToHandlersByName.apply(target);MapmethodToHandler=newLinkedHashMap();ListdefaultMethodHandlers=newLinkedList();for(Methodmethod.type:tar).getMethods()){if(method.getDeclaringClass()==Object.class){continue;}elseif(Util.isDefault(method)){DefaultMethodHandlerhandler=newDefaultMethodHandler(method);defaultMethodHandlers.add(handler);methodToHandler。put(method,handler);}else{methodToHandler.put(method,nameToHandler.get(Feign.configKey(target.type(),method)));}}//2.生成target.typejdk动态代理对象InvocationHandlerhandler=factory.create(target,methodToHandler);Tproxy=(T)Proxy.newProxyInstance(target.type().getClassLoader(),newClass>[]{target.type()},处理r);for(DefaultMethodHandlerdefaultMethodHandler:defaultMethodHandlers){defaultMethodHandler.bindTo(proxy);}returnproxy;}总结:newInstance生成JDK的动态代理,从factory.create(target,methodToHandler)我们也可以看出InvocationHandler其实是委托给了methodToHandler方法默认由SynchronousMethodHandler.Factory工厂类创建。MethodHandler方法执行器ParseHandlersByName.apply为每个方法生成执行器MethodHandler,其中最重要的一步就是通过Contract解析MethodMetadata。publicMapapply(Targetkey){//1.contract解析接口类MethodMetadataListmetadata=contract.parseAndValidateMetadata(key.type());Mapresult=中的方法和注解newLinkedHashMap();for(MethodMetadatamd:metadata){//2.buildTemplate实际上是将Method方法的参数转换成RequestBuildTemplateByResolvingArgsbuildTemplate;if(!md.formParams().isEmpty()&&md.template()。bodyTemplate()==null){//2.1形式buildTemplate=newBuildFormEncodedTemplateFromArgs(md,encoder,queryMapEncoder);}elseif(md.bodyIndex()!=null){//2.2@Body注解buildTemplate=newBuildEncodedTemplateFromArgs(md,encoder,queryMapEncoder);}else{//2.3其余的buildTemplate=newBuildTemplateByResolvingArgs(md,queryMapEncoder);}//3.将元数据和buildTemplate封装成MethodHandlerresult.put(md.configKey(),factory.create(key,md,buildTemplate,options,decoder,errorDecoder));}returnresult;}总结:该方法包括以下步骤:方法解析MethodMetadata(*),让各种RE适配不同的ContractsST声明式规范buildTemplate实际上是将Method方法的参数转换为Request。将元数据和buildTemplate封装到MethodHandler中。这样通过以上三步就创建了一个Target.type的代理对象proxy。这个代理对象可以像访问普通方法一样发送Http请求。其实和RPC的Stub模型是一样的。了解了代理之后,其实它的执行过程就很清楚了。Feign调用流程FeignInvocationHandler#invokeprivatefinalMapdispatch;publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{...//每个Method方法对应一个MethodHandlerreturndispatch.get(method).invoke(args);}总结:和上面的结论一样,实际的执行逻辑其实是委托给MethodHandler。SynchronousMethodHandler#invoke//发起http请求,根据retryer重试.clone();//调用client.execute(request,options)while(true){try{returnexecuteAndDecode(template,options);}catch(RetryableExceptione){try{//重试机制retryer.continueOrPropagate(e);}catch(RetryableExceptionth){...}continue;}}}总结:invoke主要实现请求失败的重试机制,具体执行过程委托给executeAndDecode方法。//一个是编码生成Request;另一个是http请求;三是解码生成ResponseObjectexecuteAndDecode(RequestTemplatetemplate,Optionsoptions)throwsThrowable{//1。调用拦截器RequestInterceptor,生成RequestRequestrequest=targetRequest(template);//2.httpRequestResponseresponse=client.execute(request,options);//3.response解码if(Response.class==metadata.returnType()){byte[]bodyData=Util.toByteArray(response.body().asInputStream());returnresponse.toBuilder().body(bodyData).build();}...}RequesttargetRequest(RequestTemplatetemplate){//执行拦截器for(RequestInterceptorinterceptor:requestInterceptors){interceptor.apply(template);}//生成Requestreturntarget.apply(template);这就是nativefeign的调用过程。一般来说,它分为两部分。一是客户端的封装,二是调用方法的封装。SpringCloudFeign的原理分析我们前面看了feign原著之后?理解SpringCloud的Feign非常简单。我们知道SpringCloud是基于SpringBoot的,SpringBoot也是基于Spring的。那么Spring就是一个胶水框架,封装了各种组件,就简单多了。好了,小六六这里就不一一介绍SpringCloud是如何使用Feign的了。小六六默认大家的理解,哈哈,那我们直接说原理吧。让我们考虑一下工作原理。我们平时使用feign的时候加上SpringCloudOpenFei会是一个什么样的过程呢?gn的依赖在SpringBoot启动类中添加注解@EnableFeignCleits。按照Feign的规则定义接口DemoService。在需要使用Feign接口DemoService的地方添加@FeignClient注解。直接使用@Autowire注入,使用接口完成对服务端的调用。那么我们根据这些步骤来分析分析。本文不会深入每一行的源码。当SpringBoot应用启动时,@EnableFeignClient注解的处理逻辑会触发程序扫描classPath中所有被@FeignClient注解的类。这里以XiaoLiuLiuService为例,将这些类解析成BeanDefinition,注册到Spring容器中。当Spring容器使用Feign接口将XiaoLiuLiuService注入到一些bean中时,Spring会尝试从容器中寻找XiaoLiuLiuService的实现类。由于我们没有写过XiaoLiuLiuService类的实现,所以上面步骤得到的XiaoLiuLiuService的实现类肯定是feign框架通过扩展spring的Bean处理逻辑为XiaoLiuLiuService创建一个动态接口代理对象。这里我们称之为XiaoLiuLiuServiceProxy,注册到spring容器中。Spring最终将XiaoLiuLiuService的实例注入到XiaoLiuLiuServiceProxy的Bean中。当业务请求真正发生时,将对XiaoLiuLiuService的调用统一转发给Feign框架实现的InvocationHandler。InvocationHandler负责将接口中的输入参数转换成HTTP形式发送给服务器。最后,它解析HTTP响应并将结果转换为Java对象并返回。所以我们基于原生的feign来分析分析。其实还有2个步骤。前面的nativefeign会帮我们生成一个代理对象。这是我们调用方法的主体,这个代理对象是可以请求http请求的。只要想办法把这种类型的对象放在springcontext中,那么我们下次调用的时候,这种对象当然就具备了http请求的能力了。最后,我是小六六。三天打鱼,两天发帖。今天我的分享就到这里了。