注:Platform类继承了BeanFactoryPostProcessor,在容器加载该类时执行postProcessBeanFactory,初始化一些配置注解(比如@Plugin,@handPublishPlatformContext等等), 下文中的的*Plugin类,所以在Platform类加载完毕的时候也已加载完毕了
1.EsbContextImpl.getEsbService(Class cls)
- 根据传入的esb函数所在客户端类在esbStubMethodCache(在系统中注册的esb函数)集合中获取EsbMethodStub对象,没有的话,加载该类,扫描类中带有@EsbMethod注解的函数, 构造EsbMethodStub对象(对象的属性包含:serviceId[该esb服务的id]、description[描述信息字符串:包含esb服务id,函数名称,函数入参]、 policy[调用信息对象:是否重试(默认NotRetrayable),调用协议(默认http),路由方式(默认UserHash)])。 然后使用method对象作为key,EsbMethodStub对象对象作为value,放入esbStubMethodCache集合中。
- 使用动态代理生成esb客户端类的代理类,重载InvocationHandler函数,增加一些额外操作(比如权限校验等等),这个代理类封装了esb的具体调用细节。
- 以esb客户端类的类对象为key,代理类为value放入esbStubMethodCache集合中,并返回代理类.
2.EsbMethodStub.invoke(Object[] args)【第一步生成的代理类】
-
生成invokeSessionId
-
从goalbalContext上下文容器中获取EsbPlugin类为key的上下文,执行waitForInit函数。 EsbPlugin.waitForInit():
-
1、判断initState字段是否为true,如果为true,则代表初始化过了,如果为否,判断initFail是否为true,如果为true,则代表初始化失败,否则线程休眠15秒等待初始化。 2、值得注意EsbPlugin实现onApplicationEvent函数监听了ApplicationEvent,spring容器启动后就执行onApplicationEvent调用initEsb函数。
-
initEsb():
-
读取权限校验文件
-
根据instanceId或ip,初始化当前实例信息(instanceId实例id,clusterId集群id)到EsbContextImpl,当前实例的esb函数权限到EsbSecurityContext (这些信息都是配置在umdb里(类似配置中心的概念))
-
如果没有instanceId或ip则需要先获取ip
-
构造RpcInvocation对象(里面就封装了个Map),设置ModuleInstanceId(系统实例id)、ModuleInstanceIdSign(系统实例id签名)、instanceKeyId、IsEsbReq、 RpcInvokeId、RpcSystemId(调用系统id[6004])、ServiceId(esb函数id)、ServiceDescription(esb描述信息)、ArgArray(esb函数参数)、isLocalEsbReq(是否为自己消费自己本地esb)、
-
从goalbalContext上下文容器中获取StubPlugin类为key的上下文对象,执行对象里面的invoke函数。
3. StubPlugin.invoke(RpcPolicy policy,RpcInvocation invoke):
-
继续构造从EsbMethodStub中传进来的RpcInvocation对象。设置CURRENT_USER、VIRTUAL_USER、VIRTUAL_USER_PWD等等
-
调用**stubInterceptorChain.interceptor()**执行下一步流程,stubInterceptorChain在StubPlugin初始化的时候就已经赋值了。
-
initPlugin();
-
从goalbalContext上下文容器中获取ExtensionPointRegistry类为key的上下文对象
-
从extensionPointRegistry中获取key为StubInterceptorExtensionPoint(也就是StubPlugin注解的值)的ExtensionPoint集合
-
初始化一个默认的stubInterceptorChain(DefaultStubInterceptor),判断ExtensionPoint集合是否有值,如果有值,说明有其他地方注解了StubInterceptorExtensionPoint, 那么初始化一条调用链出来,DefaultStubInterceptor处于调用链末尾。
-
赋值给stubInterceptorChain
4.StubInterceptorChain.interceptor(policy,invoke, result):
-
从goalbalContext上下文容器中获取CommunicationServiceRegistry类为key的上下文对象communicationService。
-
调用getCommunicationService设置communicationService中的传输协议(http)。
-
调用**communicationService.communication(invoke, result,policy)**执行下一步流程。
5.CommunicationService.communication(invoke, result,policy):
-
判断policy中的methodRoutePolicy(默认UserHash),来选中对应的策略。
-
判断IsEsbReq是否为ture,如果该值为空...
-
从EsbHttpEndPointRegistry根据ServiceId(esb函数id)中获取endPoint(函数执行实例),如果endPoint为空,则调用**EsbHttpEndPointRegistry.initHttpServiceEndPoint(ServiceId)**来初始化 对应的endPoint。
6.EsbHttpEndPointRegistry.initHttpServiceEndPoint(ServiceId):
-
根据ServiceId在endpoints集合中能否找到对应endPoint,如果不能,使用synchronized对endpoints加锁,进行初始化。
-
根据在ServiceId在endpointsLastInitTime中获取上次初始化时间,如果该时间不为空,判断当前时间和上次初始化时间是否在15秒内,如果是,则返回。
-
将当前时间put进endpointsLastInitTime中,然后调用PlatformContext.getRuntimeConfig().get(sid+".hosts")获取配置文件中的esb函数的配置地址 这个配置文件为cfg.properties。
-
如果获取不到hosts,那么执行**initByEsbRegisterCenter(ServiceId)**来初始化配置中心(一般不会出现这种情况,因为在容器部署的时候, docker会执行脚本从配置中心中拉取地址写入到配置文件cfg.properties中)。
initByEsbRegisterCenter(ServiceId): -
[1]、判断是否为com.cmrh.sf.esb开头的esb,这个开头的esb是内部esb,一般是初始化发起的esb。如果是,先再从配置文件拉取host,如果还没有, 则直接置为rc.cmrh.com/RH\_RC/,如果在… 然后将ttpServiceEndPoint放进endpoints集合中
-
[2]、如果不是,则调用EsbPlugin.waitForInit查看是否初始化esb失败,并等待15秒。
-
[3]、获取esbRegistryService对象,这个类包含了基本的esb函数,比如:
com.cmrh.sf.esb.queryServiceInfo、com.cmrh.sf.esb.initInstance、 com.cmrh.sf.esb.initInstanceById、com.cmrh.sf.esb.queryClusterEsbPermissions、com.cmrh.sf.esb.queryClusterInstanceInfo、 com.cmrh.sf.esb.queryAcessClusterInsts、com.cmrh.sf.esb.queryInstanceDescription -
[4]、调用esbRegistryService.queryServiceInfo()查询esb信息,这个就是去umdb库里面查配置的esb信息了。然后进行esb信息初始化.....
-
初始化完esb信息后,endpoints集合中已经有对应ServiceId的endpoint了,如果还没有对应的信息,就会抛出****Has No End Point的异常。
-
获取完endpoint之后,判断MethodRoutePolicy类型
-
[1]、UserHash:获取当前用户id,将invoke的负载均衡策略设置为UserHash(根据用户的hashCode选择),并调用 JettyHttpComponent.userHashInvoke(signature, endPoint,user.hashCode(),invoke,rpcInvocationResult,policy) 执行下一步
-
[2]、RoundRonbin,将invoke的负载均衡策略设置为RoundRonbin(轮询),并调用JettyHttpComponent.roundRonbinInvoke(signature, endPoint,invoke,rpcInvocationResult,policy)
7.JettyHttpComponent.roundRonbinInvoke(signature,endPoint,
invoke, rpcInvocationResult,policy):
- 获取endPoint对象中的activeEndPoints集合,然后调用它的next()函数,这个函数会实现轮询的负载均衡策略。
next(): 使用synchronized加锁函数,判断其中的activeEndPoints集合size,并使用类变量index取余,获取到对应的endpoint。 - 调用**JettyHttpComponent.invoke(invoke,rpcInvocationResult,ep.getUrl(),signature,policy);
**
8.JettyHttpComponent.invoke(invoke,rpcInvocationResult,ep.getUrl(),signature,policy):
- 构造ContentExchange对象,设置post方法,构造url(host/rpc/ServiceId 例如:/RH_UW/rpc/com.cmrh.uw.getGNCPolicyDeliveryContactInfo) 构造头部信息(RpcEncode:JavaFormat、RPC_REQ_FLAG:JavaRpc等等)
- PlatformContext.getGoalbalContext(JettyHttpClient.class, JettyHttpClient.class). getHttpClient(policy.getMethodCommunicationComponent()) .send(exchange);获取http客户端并执行。
- 同步调用,等待返回结果,如果返回7,则代表调用成功,然后构造RpcInvocationResult result返回结果对象。
- 使用SerializableServiceRegistryImpl序列化实现类,构造RpcInvocationResult result返回结果对象。 RpcInvocationResult result=(RpcInvocationResult) serializableServiceRegistry.getSerializableService( exchange.getResponseFields().getStringField(RpcInvocationConst.RpcEncode)).fromBytes(exchange.getResponseContentBytes()); rpcInvocationResult.getValueMap().putAll(result.getValueMap());
- 调用完毕。
8.返回到第三步的StubPlugin.invoke(RpcPolicy policy,RpcInvocation invoke)函数中去,这里构造了RpcInvocationResult result并一步一步传导了最后, 在这个函数返回result.
9.返回到EsbMethodStub.invoke(Object[] args)中,将调用具体信息结果(调用方,被调用方,调用时间,调用花费等等)打印在httpAuditLogger日志中。
esb测活定时任务类:
HttpEndPointRecoverTask,这个类会定时访问注册在系统中的esb接口(要调用的HttpServiceEndPoint),如果有失活接口,则记录日志并更新endpoint中 activeEndPoints和failEndPoints集合,并更新上次刷新时间(调用esb接口的时候会访问这两个集合,结合刷新时间判断esb接口是否存在活跃节点)
esb服务监控类:
ServiceProviderMonitorTask,这个类会定时去umdb查询对应esb接口有没有被下线,如果有下线的就更新HttpServiceEndPoint的集合