关于jsf:JSF源码分析一
作者:京东批发 李孟冬架构设计 1.7.4-HOTFIX-T4版本包布局及简要含意 看过了全包的简要,那么其外围的功能模块,就从罕用的我的项目xml配置登程,便于咱们的了解。如下: jsf-provider.xml配置以咱们地址服务的jsf-provider.xml文件为例,即: 能够看到,在JSF的配置文件中,咱们并没有看到任何对于注册核心的内容。说到底,作为(团体自主研发的高效)RPC调用框架,其高可用的注册核心重中之重,所以带着这份纳闷,持续往下探索,没有注册核心地址,这些标签是怎么实现服务的注册,订阅的。 配置解析在Spring的体系中,Spring提供了可扩大Schema的反对,即自定义的标签解析。 1、首先咱们发现配置文件中自定义的xsd文件,在标签名称上找到NamespaceUri链接http://jsf.jd.com/schema/jsf/jsf.xsd 2、而后依据SPI加载,在META-INF中找到定义好Spring.handlers文件和Spring.schemas文件,一个是具体的解析器的配置,一个是jsf.xsd的具体门路 Spring.handlers文件内容:http\://jsf.jd.com/schema/jsf=com.jd.jsf.gd.config.spring.JSFNamespaceHandler--------------------------------------------------------Spring.schemas文件内容:http\://jsf.jd.com/schema/jsf/jsf.xsd=META-INF/jsf.xsd3、由此咱们进一步查问继承NameSpaceHanderSupport或者实现NameSpaceHandler对应的接口类,在咱们的jsf框架中JSFNamespaceHandler是采纳继承前者(NameSpaceHanderSupport)去实现的,即: com.jd.jsf.gd.config.spring.JSFNamespaceHandler 【补充】NamespaceHandler的性能就是解析咱们自定义的JSF命名空间的,为了不便起见,咱们实现NamespaceHandlerSupport,其外部通过BeanDefinitionParser对具体的标签进行解决,即对咱们定义的标签进行具体解决。 com.jd.jsf.gd.config.spring.JSFBeanDefinitionParser#parse 4、最终这些配置(也就是咱们在xml中配置的标签值)会解析成为ServerConfig和ProviderConfig,并且会根据所配置的属性,对相应的类进行属性的赋值。 com.jd.jsf.gd.config.ServerConfig com.jd.jsf.gd.config.ProviderConfig 初始化OK,咱们回到JSFNamespaceHandler看一下服务是如何裸露的。家喻户晓,在Spring容器中的bean肯定会经验一个初始化的过程。所以通过com.jd.jsf.gd.config.spring.JSFBeanDefinitionParser实现org.springframework.beans.factory.xml.BeanDefinitionParser来进行xml的解析,另外通过ParserContext中封装了BeanDefinitionRegistry对象,用于BeanDefinition的注册,用来初始化各个bean,即(com.jd.jsf.gd.config.spring.JSFBeanDefinitionParser#parse)。 如下:bean类ProviderBean会监听上下文事件,并且当整个容器初始化结束之后会调用export()办法进行服务的裸露。 com.jd.jsf.gd.config.spring.ProviderBean 服务裸露咱们回到源码中,发现其外围代码逻辑如下图: com.jd.jsf.gd.config.ProviderConfig#doExport com.jd.jsf.gd.config.ProviderConfig#doExport 该办法的整体逻辑如下: 1、首先进行各种根本的校验和拦挡,如: [JSF-21200]provider的alias不能为空;[JSF-21202]providerconfig的server为空;[JSF-21203]同一接口+alias的provider配置了多个;...2、其次获取所有的RegistryConfig,如果获取不到注册的地址,那么就会走默认的注册核心地址:“i.jsf.jd.com”。 com.jd.jsf.gd.config.RegistryConfig 3、而后获取provider中配置的server,如果provider中存在server相干的配置,即是com.jd.jsf.gd.config.ServerConfig,此时会启动server(serverConfig.start()),并且采纳默认对应的序列化形式(serverConfig.getSerialization(),默认msgpack)进行注册服务编码。 com.jd.jsf.gd.config.ServerConfig#start(服务启动) 4、start办法中会调用ServerFactory中的办法产生绝对应的对象,而后会调用Server中的办法去启动Server,而在这个过程中,最终会对应到ServerTransportFactory产生相应的传输层,即是定位到JSFServerTransport的start()办法,在这里咱们能够看到,该局部实现了netty框架的transport层,在进入这个办法的时候,会依据是否配置应用epoll模型来抉择所生成的对象是EpollServerSocketChannel或者NioServerSocketChannel,而后在ServerBootStrap中初始化相干的参数,直到最初绑定好端口号。 com.jd.jsf.gd.server.ServerFactory#initServer com.jd.jsf.gd.transport.JSFServerTransport#start 5、最初通过(this.register();)服务注册并且裸露进来,其中JsfRegistry这个类的对象,在该类的构造函数中会连贯jsf的注册核心,如果注册核心不可用的话,会生成并应用本地的文件并且开始守护线程,并应用两个线程池去发送心跳检测以及重试机制,另外一个线程池去检测连贯是否胜利(com.jd.jsf.gd.registry.JSFRegistry#addRegistryStatListeners)。 com.jd.jsf.gd.registry.JSFRegistry 6、服务注册(com.jd.jsf.gd.registry.JSFRegistry#register)的过程会将对应的ProviderConfig转换为JsfUrl类,这里JsfUrl是整个框架的外围,他保留了一系列的配置,并且和其同样重要的还有订阅Url类SubscribeUrl,这里JsfUrl属于服务Url,服务Url中保留了协定,端口号,ip地址等相干重要的信息,并且回到下层JsfContext会将配置信息保护起来(JSFContext.cacheProviderConfig(this);)。到这里(this.exported = true;)provider的服务从配置拆卸到服务裸露就实现了。 com.jd.jsf.gd.registry.JSFRegistry#register com.jd.jsf.vo.JsfUrl jsf-consumer.xml配置以上是实现了provider服务的裸露,那么咱们回到consumer中,看一下,如下: 咱们在上方的配置文件中发现到了注册核心地址i.jsf.jd.com,也就是说服务注册相干的配置没有写到jsf-provider.xml端,只是配置到了jsf-consumer.xml中而已。 配置解析&初始化配置解析过程是同上,就不多做赘述了,最终这些配置会解析成为ConsumerConfig和RegistryConfig,并且会根据所配置的属性,对相应的类进行属性的赋值。 com.jd.jsf.gd.config.ConsumerConfig->AbstractConsumerConfig 最终初始化映射到ConsumerBean类。 com.jd.jsf.gd.config.spring.ConsumerBean 服务订阅不过咱们发现其实现了FactoryBean,如咱们所理解的,如果一个 bean 实现了该接口(FactoryBean),它被用作一个对象的工厂来裸露,而不是间接作为一个将本人裸露的 bean 实例。这也就意味着须要调用getObject()来获取真正的实例化对象(可能是共享的或独立的)。之所以这样应用的起因在于咱们的Consumer端只能调用接口,接口是无奈间接应用的,它须要被动静代理封装,产生代理对象,再把它放入Spring容器中。因而应用FactoryBean其实只是为了不便创立代理对象而已。 在getObject()办法中ConsumerBean会调用子类consumerConfig的refer()办法,从而开始了客户端的初始化的过程。在refer()过程中,consumer会去订阅相干的provider的服务。外围代码如下: com.jd.jsf.gd.config.ConsumerConfig#refer 该办法的整体逻辑为,如下: 1、首先进行各种根本的校验和拦挡,如: consumer的alias不能为空,请查看配置同一个接口+alias+protocol 本地配置的超过三次,抛出启动异样...2、一些不同配置的逻辑,如是否泛化调用,是否走injvm调用等 3、通过工厂模式生成一个Client的实例,因为下面ConsumerConfig->AbstractConsumerConfig默认的集群策略failover,所以在没有配置的状况下会生成FailoverClient,而后进行相干的Invoke操作(this.proxyInvoker = new ClientProxyInvoker(this);)。 com.jd.jsf.gd.client.ClientFactory#getClient 【补充】目前JSF反对的集群策略有,failover:失败重试(默认);failfast:失败疏忽;pinpoint:定点调用; 4、在client中,先定义其负载平衡,而后判断是否需提早建设长链接。否的话,会间接进行初始化连贯。其次如果未定义路由规定,或者不存在连贯时,Client会先初始化相干的路由以及初始化连贯,如下: 5、在初始化连贯(initConnections)中会进行调用ConsumerConfig中的subscribe()进行服务的订阅,并且在初始化的过程中,consumer会连贯相应的Providers。 ...