服务调用者总揽:
在remoting包中的invoker包就是服务调用者,包括配置,bean代理,负载均衡策略,调用方案等。
我们先来看下 @XxlRpcReference注解,这个注解中定义了服务调用者的一些使用的一些策略。
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface XxlRpcReference {
//通讯方式 ,缺省netty
NetEnum netType() default NetEnum.NETTY;
//序列化方式 ,缺省hessian
Serializer.SerializeEnum serializer() default Serializer.SerializeEnum.HESSIAN;
// 调用方式 ,缺省sync
CallType callType() default CallType.SYNC;
// 负载均衡策略,缺省round
LoadBalance loadBalance() default LoadBalance.ROUND;
// version
String version() default "";
// 超时时间
long timeout() default 1000;
// 服务提供方地址, 这边可以自己来配置
String address() default "";
// token 做验证使用
String accessToken() default "";
//XxlRpcInvokeCallback invokeCallback() ;
}
如果你使用spring的话,就可以使用注解+XxlRpcSpringInvokerFactory 的方式来使用rpc。
首先看下该类的继承关系:
public class XxlRpcSpringInvokerFactory extends InstantiationAwareBeanPostProcessorAdapter implements InitializingBean,DisposableBean, BeanFactoryAware
继承InstantiationAwareBeanPostProcessorAdapter , 实现InitializingBean,DisposableBean,BeanFactoryAware 接口。
分别说说它们的调用时机(按照顺序):
BeanFactoryAware : 注入 beanfactory 使用。可以使用beanfactory获得 spring中bean。他这边就一个setBeanFactory 方法。
InitializingBean : 在bean实例化设置完属性后调用 afterPropertiesSet方法
InstantiationAwareBeanPostProcessorAdapter: 对实例化之后的bean进行增强。调用postProcessAfterInstantiation 方法。
DisposableBean:bean 销毁的时候调用destroy方法。
有了它们的执行顺序就一下明了了。
setBeanFactory() ----> afterPropertiesSet() ---->postProcessAfterInstantiation() ---->destroy()。
可以说前三个方法是与服务调用者初始化相关的。我们挨着看下
这个很简单,就是将beanfactory 的引用给成员变量。
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void afterPropertiesSet() throws Exception {
// start invoker factory
xxlRpcInvokerFactory = new XxlRpcInvokerFactory(serviceRegistryClass, serviceRegistryParam);
xxlRpcInvokerFactory.start();
}
创建XxlRpcInvokerFactory 对象,调用start方法。接下来我们看看这个类
2.2.2.1 XxlRpcProviderFactory
XxlRpcProviderFactory类主要是做了服务注册,客户端线程池响应初始化和回调线程池的初始化,客户端的 start,stop的工作。
我们来看下start 方法:
根据配置创建对应注册中心对象,调用注册中心的start方法。这里不详细解析。注册中心start 方法主要是干了 创建注册中心client并创建守护线程来刷新获取服务提供者列表。
public void start() throws Exception {
// start registry
if (serviceRegistryClass != null) {
// 创建 注册中心 对象
serviceRegistry = serviceRegistryClass.newInstance();
// 调用注册中心对象的 start 方法
serviceRegistry.start(serviceRegistryParam);
}
}
接下来看下stop方法:
public void stop() throws Exception {
// stop registry 停止注册中心客户端
if (serviceRegistry != null) {
serviceRegistry.stop();
}
// stop callback 执行停止回调方法
if (stopCallbackList.size() > 0) {
for (BaseCallback callback: stopCallbackList) {
try {
callback.run();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
// stop CallbackThreadPool 关闭线程池
stopCallbackThreadPool();
}
先看一下这段代码,在关键的位置加了注释,很简单也很好理解。
@Override
public boolean postProcessAfterInstantiation(final Object bean, final String beanName) throws BeansException {
// collection
final Set serviceKeyList = new HashSet<>();
// parse XxlRpcReferenceBean
ReflectionUtils.doWithFields(bean.getClass(), new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
// 字段 是否有XxlRpcReference 注解
if (field.isAnnotationPresent(XxlRpcReference.class)) {
// valid 获取字段的类型
Class iface = field.getType();
// 字段不是 接口抛出异常
if (!iface.isInterface()) {
throw new XxlRpcException("xxl-rpc, reference(XxlRpcReference) must be interface.");
}
// 获取注解信息,
XxlRpcReference rpcReference = field.getAnnotation(XxlRpcReference.class);
// init reference bean 创建 referenceBean , 设置一些需要的参数 ,将注解中的参数 设置到referenceBean 对象中。
XxlRpcReferenceBean referenceBean = new XxlRpcReferenceBean(
rpcReference.netType(),
rpcReference.serializer().getSerializer(),
rpcReference.callType(),
rpcReference.loadBalance(),
iface,
rpcReference.version(),
rpcReference.timeout(),
rpcReference.address(),
rpcReference.accessToken(),
null,
xxlRpcInvokerFactory
);
// 调用 getObject方法, 这个方法主要使用生成代理对象的。
Object serviceProxy = referenceBean.getObject();
// set bean
field.setAccessible(true);
//给这个字段赋值
field.set(bean, serviceProxy);
logger.info(">>>>>>>>>>> xxl-rpc, invoker factory init reference bean success. serviceKey = {}, bean.field = {}.{}",
XxlRpcProviderFactory.makeServiceKey(iface.getName(), rpcReference.version()), beanName, field.getName());
// collection 根据这个接口全类名与 version生成 servicekey ,跟服务提供者一眼的
String serviceKey = XxlRpcProviderFactory.makeServiceKey(iface.getName(), rpcReference.version());
serviceKeyList.add(serviceKey);
}
}
});
// mult discovery 进行服务发现, 本地缓存一下 这个key的服务提供者
if (xxlRpcInvokerFactory.getServiceRegistry() != null) {
try {
xxlRpcInvokerFactory.getServiceRegistry().discovery(serviceKeyList);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
return super.postProcessAfterInstantiation(bean, beanName);
}
该方法 主要是找出带有XxlRpcReference注解的成员变量。然后解析XxlRpcReference注解中的参数,创建XxlRpcReferenceBean对象,生成代理对象,然后将代理对象赋值给这个字段,这样,在我们使用这个service的时候,真正调用的就是这个代理对象。接下来我们要看下这个XxlRpcReferenceBean 类了。
2.2.3.1 XxlRpcReferenceBean
可以根据这个类生成代理对象,也就是我们常说的客户端stub。
首先,我们先来看下该类的字段与构造
private NetEnum netType;
private Serializer serializer; // 序列化方式,hession
private CallType callType; // 调用方式
private LoadBalance loadBalance; //负载均衡策略
private Class> iface; // 接口class对象
private String version; // 版本
private long timeout = 1000; //超时时间
private String address; // 服务提供者地址
private String accessToken;// token
private XxlRpcInvokeCallback invokeCallback;
private XxlRpcInvokerFactory invokerFactory; // invoker factory
public XxlRpcReferenceBean(NetEnum netType,
Serializer serializer,
CallType callType,
LoadBalance loadBalance,
Class> iface,
String version,
long timeout,
String address,
String accessToken,
XxlRpcInvokeCallback invokeCallback,
XxlRpcInvokerFactory invokerFactory
) {
this.netType = netType;
this.serializer = serializer;
this.callType = callType;
this.loadBalance = loadBalance;
this.iface = iface;
this.version = version;
this.timeout = timeout;
this.address = address;
this.accessToken = accessToken;
this.invokeCallback = invokeCallback;
this.invokerFactory = invokerFactory;
// valid
if (this.netType==null) {
throw new XxlRpcException("xxl-rpc reference netType missing.");
}
if (this.serializer==null) {
throw new XxlRpcException("xxl-rpc reference serializer missing.");
}
if (this.callType==null) {
throw new XxlRpcException("xxl-rpc reference callType missing.");
}
if (this.loadBalance==null) {
throw new XxlRpcException("xxl-rpc reference loadBalance missing.");
}
if (this.iface==null) {
throw new XxlRpcException("xxl-rpc reference iface missing.");
}
if (this.timeout < 0) {
this.timeout = 0;
}
if (this.invokerFactory == null) {
this.invokerFactory = XxlRpcInvokerFactory.getInstance();
}
// init Client
initClient();
}
在构造最后调用了一个 initClient方法。 这个方法主要是创建client对象,初始化参数。
然后我们再来看下,服务调用者 核心 :代理对象的生成
getObject 方法就是生成某个服务的代理对象。使用技术就是jdk的proxy。
public Object getObject() {
return Proxy.newProxyInstance(Thread.currentThread()
.getContextClassLoader(), new Class[] { iface },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// method param 生成一些 调用参数
String className = method.getDeclaringClass().getName(); // iface.getName()
String varsion_ = version;
// 方法名
String methodName = method.getName();
//方法参数类型
Class>[] parameterTypes = method.getParameterTypes();
// 方法参数值
Object[] parameters = args;
// filter for generic
// 这个就是判断是否是泛化调用,如果是泛化调用,参数就按照用户配置的来,比如说 你要调用哪个类,方法名,参数类型,参数值。这些都要
// 设定好
if (className.equals(XxlRpcGenericService.class.getName()) && methodName.equals("invoke")) {
Class>[] paramTypes = null;
if (args[3]!=null) {
String[] paramTypes_str = (String[]) args[3];
if (paramTypes_str.length > 0) {
paramTypes = new Class[paramTypes_str.length];
for (int i = 0; i < paramTypes_str.length; i++) {
paramTypes[i] = ClassUtil.resolveClass(paramTypes_str[i]);
}
}
}
className = (String) args[0];
varsion_ = (String) args[1];
methodName = (String) args[2];
parameterTypes = paramTypes;
parameters = (Object[]) args[4];
}
// 这边就是确认一下 你这方法是出自哪个类的
// filter method like "Object.toString()"
if (className.equals(Object.class.getName())) {
logger.info(">>>>>>>>>>> xxl-rpc proxy class-method not support [{}#{}]", className, methodName);
throw new XxlRpcException("xxl-rpc proxy class-method not support");
}
// 服务提供者 地址的选择, 根据负载均衡策略来选择
String finalAddress = address;
if (finalAddress==null || finalAddress.trim().length()==0) {
if (invokerFactory!=null && invokerFactory.getServiceRegistry()!=null) {
// discovery
String serviceKey = XxlRpcProviderFactory.makeServiceKey(className, varsion_);
TreeSet addressSet = invokerFactory.getServiceRegistry().discovery(serviceKey);
// load balance
if (addressSet==null || addressSet.size()==0) {
// pass
} else if (addressSet.size()==1) { // 就一个的时候,那就选第一个
finalAddress = addressSet.first();
} else { // 负载均衡
finalAddress = loadBalance.xxlRpcInvokerRouter.route(serviceKey, addressSet);
}
}
}
// 最后调用地址还是 null 就抛出异常了
if (finalAddress==null || finalAddress.trim().length()==0) {
throw new XxlRpcException("xxl-rpc reference bean["+ className +"] address empty");
}
// request 请求参数封住
XxlRpcRequest xxlRpcRequest = new XxlRpcRequest();
xxlRpcRequest.setRequestId(UUID.randomUUID().toString());
xxlRpcRequest.setCreateMillisTime(System.currentTimeMillis());
xxlRpcRequest.setAccessToken(accessToken);
xxlRpcRequest.setClassName(className);
xxlRpcRequest.setMethodName(methodName);
xxlRpcRequest.setParameterTypes(parameterTypes);
xxlRpcRequest.setParameters(parameters);
// send 根据调用策略 调用
if (CallType.SYNC == callType) {
// future-response set
XxlRpcFutureResponse futureResponse = new XxlRpcFutureResponse(invokerFactory, xxlRpcRequest, null);
try {
// do invoke
client.asyncSend(finalAddress, xxlRpcRequest);
// future get
XxlRpcResponse xxlRpcResponse = futureResponse.get(timeout, TimeUnit.MILLISECONDS);
if (xxlRpcResponse.getErrorMsg() != null) {
throw new XxlRpcException(xxlRpcResponse.getErrorMsg());
}
return xxlRpcResponse.getResult();
} catch (Exception e) {
logger.info(">>>>>>>>>>> xxl-rpc, invoke error, address:{}, XxlRpcRequest{}", finalAddress, xxlRpcRequest);
throw (e instanceof XxlRpcException)?e:new XxlRpcException(e);
} finally{
// future-response remove
futureResponse.removeInvokerFuture();
}
} else if (CallType.FUTURE == callType) {
// future-response set
XxlRpcFutureResponse futureResponse = new XxlRpcFutureResponse(invokerFactory, xxlRpcRequest, null);
try {
// invoke future set
XxlRpcInvokeFuture invokeFuture = new XxlRpcInvokeFuture(futureResponse);
XxlRpcInvokeFuture.setFuture(invokeFuture);
// do invoke
client.asyncSend(finalAddress, xxlRpcRequest);
return null;
} catch (Exception e) {
logger.info(">>>>>>>>>>> xxl-rpc, invoke error, address:{}, XxlRpcRequest{}", finalAddress, xxlRpcRequest);
// future-response remove
futureResponse.removeInvokerFuture();
throw (e instanceof XxlRpcException)?e:new XxlRpcException(e);
}
} else if (CallType.CALLBACK == callType) {
// get callback
XxlRpcInvokeCallback finalInvokeCallback = invokeCallback;
XxlRpcInvokeCallback threadInvokeCallback = XxlRpcInvokeCallback.getCallback();
if (threadInvokeCallback != null) {
finalInvokeCallback = threadInvokeCallback;
}
if (finalInvokeCallback == null) {
throw new XxlRpcException("xxl-rpc XxlRpcInvokeCallback(CallType="+ CallType.CALLBACK.name() +") cannot be null.");
}
// future-response set
XxlRpcFutureResponse futureResponse = new XxlRpcFutureResponse(invokerFactory, xxlRpcRequest, finalInvokeCallback);
try {
client.asyncSend(finalAddress, xxlRpcRequest);
} catch (Exception e) {
logger.info(">>>>>>>>>>> xxl-rpc, invoke error, address:{}, XxlRpcRequest{}", finalAddress, xxlRpcRequest);
// future-response remove
futureResponse.removeInvokerFuture();
throw (e instanceof XxlRpcException)?e:new XxlRpcException(e);
}
return null;
} else if (CallType.ONEWAY == callType) {
client.asyncSend(finalAddress, xxlRpcRequest);
return null;
} else {
throw new XxlRpcException("xxl-rpc callType["+ callType +"] invalid");
}
}
});
}
这个方法虽然看起来非常的长,但是也不是很麻烦,主要干了这些事情:
1⃣️.获取请求的一些参数,包括你要调哪个类,哪个方法,方法参数类型,方法参数值。
2⃣️. 如果是泛化调用(你自己指定1⃣️里面的字段)类,方法名,方法参数类型,方法参数 就要按照你指定的来。
3⃣️.服务提供者地址的选择,根据负载均衡策略来,你自己在注解中配置的优先级最低(这个有点想不通)。
4⃣️.封装请求参数,将类,方法名,方法参数类型,方法参数值,还有一个唯一的uuid ,封装成Request对象实体。
5⃣️.根据调用策略来进行调用。
本文章主要是讲了xxl-rpc 服务调用者生成的大体流程,从配置到注册中心的连接 到 生成referencebean 再到 生成代理对象。通过这篇文章我们能够了解到rpc服务调用方其实就是在本服务生成了一个调用对象。 这里并没有深入的讲解其中的细节,比如说调用策略,客户端网络通信,负载均衡等等。这些后续章节会详细讲解。