我们在前面讲的服务调用里面会发现有这样一段代码:
// 创建代理类
proxyIns = (T) ProxyFactory.buildProxy(consumerConfig.getProxy(), consumerConfig.getProxyClass(),proxyInvoker);
这段代码就是我们说的根据SPI加载代理类,这个代理类可以在服务调用时候指定,jdk或者javassist两种,这两种区别我这里简单讲一下:jdk代理是基于反射生成代理类的,所以必须要求代理类是接口或者抽象类,而javassist是基于ASM字节码技术的,不需要限定类的类型,它是为代理类使用asm技术生成子类,这个是大致的区别,那我们来看下我们调用的时候到底是怎么调用的,是怎么使用的负载均衡呢?我们就以JDK代理为例吧!
// 创建代理类
proxyIns = (T) ProxyFactory.buildProxy(consumerConfig.getProxy(), consumerConfig.getProxyClass(),proxyInvoker);
/**
* 构建代理类实例
*
* @param proxyType 代理类型
* @param clazz 原始类
* @param proxyInvoker 代码执行的Invoker
* @param <T> 类型
* @return 代理类实例
* @throws Exception
*/
public static <T> T buildProxy(String proxyType, Class<T> clazz, Invoker proxyInvoker) throws Exception {
try {
// 使用spi加载代理类,没设置则使用的javassist
ExtensionClass<Proxy> ext = ExtensionLoaderFactory.getExtensionLoader(Proxy.class)
.getExtensionClass(proxyType);
if (ext == null) {
throw ExceptionUtils.buildRuntime("consumer.proxy", proxyType,
"Unsupported proxy of client!");
}
Proxy proxy = ext.getExtInstance();
return proxy.getProxy(clazz, proxyInvoker);
} catch (SofaRpcRuntimeException e) {
throw e;
} catch (Throwable e) {
throw new SofaRpcRuntimeException(e.getMessage(), e);
}
}
/**
* Proxy implement base on jdk
*
* @author <a href=mailto:zhanggeng.zg@antfin.com>GengZhang</a>
*/
@Extension("jdk")
public class JDKProxy implements Proxy {
@Override
public <T> T getProxy(Class<T> interfaceClass, Invoker proxyInvoker) {
InvocationHandler handler = new JDKInvocationHandler(interfaceClass, proxyInvoker);
ClassLoader classLoader = ClassLoaderUtils.getCurrentClassLoader();
// 通过反射获取数据
T result = (T) java.lang.reflect.Proxy.newProxyInstance(classLoader,
new Class[] { interfaceClass }, handler);
return result;
}
@Override
public Invoker getInvoker(Object proxyObject) {
return parseInvoker(proxyObject);
}
/**
* Parse proxy invoker from proxy object
*
* @param proxyObject Proxy object
* @return proxy invoker
*/
public static Invoker parseInvoker(Object proxyObject) {
InvocationHandler handler = java.lang.reflect.Proxy.getInvocationHandler(proxyObject);
if (handler instanceof JDKInvocationHandler) {
return ((JDKInvocationHandler) handler).getProxyInvoker();
}
return null;
}
}
既然获得了一个代理对象,那我们就看下他是怎么处理请求的吧!JDKInvocationHandler#invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] paramValues)
throws Throwable {
// 获取方法名称
String methodName = method.getName();
// 获取方法参数
Class[] paramTypes = method.getParameterTypes();
// 如果是调用toString、hashCode、equals 则直接调用本类的方法不进行远程调用
if ("toString".equals(methodName) && paramTypes.length == 0) {
return proxyInvoker.toString();
} else if ("hashCode".equals(methodName) && paramTypes.length == 0) {
return proxyInvoker.hashCode();
} else if ("equals".equals(methodName) && paramTypes.length == 1) {
Object another = paramValues[0];
return proxy == another ||
(proxy.getClass().isInstance(another) && proxyInvoker.equals(JDKProxy.parseInvoker(another)));
}
// 构建请求
SofaRequest sofaRequest = MessageBuilder.buildSofaRequest(method.getDeclaringClass(),
method, paramTypes, paramValues);
// invoke调用开始
SofaResponse response = proxyInvoker.invoke(sofaRequest);
if (response.isError()) {
throw new SofaRpcException(RpcErrorType.SERVER_UNDECLARED_ERROR, response.getErrorMsg());
}
Object ret = response.getAppResponse();
if (ret instanceof Throwable) {
throw (Throwable) ret;
} else {
if (ret == null) {
return ClassUtils.getDefaultPrimitiveValue(method.getReturnType());
}
return ret;
}
}
proxyInvoker.invoke(sofaRequest);这句话就涉及到我们初始化代理invoker的时候我们返回的是ClientProxyInvoker,所以我们点进ClientProxyInvoker#invoke
/**
* proxy拦截的调用
*
* @param request 请求消息
* @return 调用结果
*/
@Override
public SofaResponse invoke(SofaRequest request) throws SofaRpcException {
SofaResponse response = null;
Throwable throwable = null;
try {
// 将请求的上下文放入Deque
RpcInternalContext.pushContext();
RpcInternalContext context = RpcInternalContext.getContext();
context.setProviderSide(false);
// 包装请求
decorateRequest(request);
try {
// 产生开始调用事件
if (EventBus.isEnable(ClientStartInvokeEvent.class)) {
EventBus.post(new ClientStartInvokeEvent(request));
}
// 得到结果
response = cluster.invoke(request);
} catch (SofaRpcException e) {
throwable = e;
throw e;
} finally {
// 产生调用结束事件
if (!request.isAsync()) {
if (EventBus.isEnable(ClientEndInvokeEvent.class)) {
EventBus.post(new ClientEndInvokeEvent(request, response, throwable));
}
}
}
// 包装响应
decorateResponse(response);
return response;
} finally {
RpcInternalContext.removeContext();
RpcInternalContext.popContext();
}
}
我们先来看下包装请求decorateRequest(request),因为前面设置的invoker是DefaultClientProxyInvoker所以调用的是这个方法
DefaultClientProxyInvoker#decorateRequest,而不是ClientProxyInvoker#decorateRequest,这个方法是空的
@Override
protected void decorateRequest(SofaRequest request) {
// 公共的设置,空的
super.decorateRequest(request);
// 缓存是为了加快速度
request.setTargetServiceUniqueName(serviceName);
// 设置序列化类型
request.setSerializeType(serializeType == null ? 0 : serializeType);
// 是否是泛化调用,限于篇幅泛化调用的概念可以先自行百度一下
if (!consumerConfig.isGeneric()) {
// 找到调用类型, generic的时候类型在filter里进行判断
request.setInvokeType(consumerConfig.getMethodInvokeType(request.getMethodName()));
}
// 取出第一个context,如果我们需要使用回调和透传参数的话这个就会拿到值,否则如果不提前设置的话都是null的
// 比如 RpcInvokeContext.getContext.setsetResponseCallback或putRequestBaggage这种等
RpcInvokeContext invokeCtx = RpcInvokeContext.peekContext();
// 取出我们上面存储的上下文context
RpcInternalContext internalContext = RpcInternalContext.getContext();
if (invokeCtx != null) {
// 如果用户设置了调用级别回调函数
SofaResponseCallback responseCallback = invokeCtx.getResponseCallback();
if (responseCallback != null) {
request.setSofaResponseCallback(responseCallback);
invokeCtx.setResponseCallback(null); // 一次性用完
invokeCtx.put(RemotingConstants.INVOKE_CTX_IS_ASYNC_CHAIN,
isSendableResponseCallback(responseCallback));
}
// 如果用户设置了调用级别超时时间
Integer timeout = invokeCtx.getTimeout();
if (timeout != null) {
request.setTimeout(timeout);
invokeCtx.setTimeout(null);// 一次性用完
}
// 如果用户指定了调用的URL
String targetURL = invokeCtx.getTargetURL();
if (targetURL != null) {
internalContext.setAttachment(HIDDEN_KEY_PINPOINT, targetURL);
invokeCtx.setTargetURL(null);// 一次性用完
}
// 如果用户指定了透传数据
if (RpcInvokeContext.isBaggageEnable()) {
// 需要透传
BaggageResolver.carryWithRequest(invokeCtx, request);
internalContext.setAttachment(HIDDEN_KEY_INVOKE_CONTEXT, invokeCtx);
}
}
// 是否开启附件传递功能
if (RpcInternalContext.isAttachmentEnable()) {
internalContext.setAttachment(INTERNAL_KEY_APP_NAME, consumerConfig.getAppName());
internalContext.setAttachment(INTERNAL_KEY_PROTOCOL_NAME, consumerConfig.getProtocol());
}
// 额外属性通过HEAD传递给服务端
request.addRequestProp(RemotingConstants.HEAD_APP_NAME, consumerConfig.getAppName());
request.addRequestProp(RemotingConstants.HEAD_PROTOCOL, consumerConfig.getProtocol());
}
我们再来看下他们的invoke方法,首先调用的是AbstractCluster#invoke方法:
@Override
public SofaResponse invoke(SofaRequest request) throws SofaRpcException {
SofaResponse response = null;
try {
// 做一些初始化检查,例如未连接可以连接
checkClusterState();
// 开始调用
countOfInvoke.incrementAndGet(); // 计数+1
// 调用实现类的doInvoke
response = doInvoke(request);
return response;
} catch (SofaRpcException e) {
// 客户端收到异常(客户端自己的异常)
throw e;
} finally {
countOfInvoke.decrementAndGet(); // 计数-1
}
}
我们可以从前面的DefaultConsumerBootstrap中看到他这里加载的cluster默认是取得配置文件的FailoverCluster#doInvoke
public SofaResponse doInvoke(SofaRequest request) throws SofaRpcException {
String methodName = request.getMethodName();
// 获取配置的失败重试次数
int retries = consumerConfig.getMethodRetries(methodName);
int time = 0;
SofaRpcException throwable = null;// 异常日志
List<ProviderInfo> invokedProviderInfos = new ArrayList<ProviderInfo>(retries + 1);
do {
// 选择调用的provider,这里也就是我们讲的负载均衡的选择啦
ProviderInfo providerInfo = select(request, invokedProviderInfos);
try {
// 执行过滤器链,得到返回结果
SofaResponse response = filterChain(providerInfo, request);
if (response != null) {
if (throwable != null) {
if (LOGGER.isWarnEnabled(consumerConfig.getAppName())) {
LOGGER.warnWithApp(consumerConfig.getAppName(),
LogCodes.getLog(LogCodes.WARN_SUCCESS_BY_RETRY,
throwable.getClass() + ":" + throwable.getMessage(),
invokedProviderInfos));
}
}
return response;
} else {
throwable = new SofaRpcException(RpcErrorType.CLIENT_UNDECLARED_ERROR,
"Failed to call " + request.getInterfaceName() + "." + methodName
+ " on remote server " + providerInfo + ", return null");
time++;
}
} catch (SofaRpcException e) { // 服务端异常+ 超时异常 才发起rpc异常重试
if (e.getErrorType() == RpcErrorType.SERVER_BUSY
|| e.getErrorType() == RpcErrorType.CLIENT_TIMEOUT) {
throwable = e;
time++;
} else {
throw e;
}
} catch (Exception e) { // 其它异常不重试
throw new SofaRpcException(RpcErrorType.CLIENT_UNDECLARED_ERROR,
"Failed to call " + request.getInterfaceName() + "." + request.getMethodName()
+ " on remote server: " + providerInfo + ", cause by unknown exception: "
+ e.getClass().getName() + ", message is: " + e.getMessage(), e);
} finally {
if (RpcInternalContext.isAttachmentEnable()) {
RpcInternalContext.getContext().setAttachment(RpcConstants.INTERNAL_KEY_INVOKE_TIMES,
time + 1); // 重试次数
}
}
invokedProviderInfos.add(providerInfo);
} while (time <= retries);
throw throwable;
}
我们一步步分析,先看下我们这边最重要的部分选择provider,也就是我们要讲的负载均衡!
/**
* 根据规则进行负载均衡
*
* @param message 调用对象
* @param invokedProviderInfos 已调用列表
* @return 一个可用的provider
* @throws SofaRpcException rpc异常
*/
protected ProviderInfo select(SofaRequest message, List<ProviderInfo> invokedProviderInfos)
throws SofaRpcException {
// 粘滞连接,当前连接可用
if (consumerConfig.isSticky()) {
if (lastProviderInfo != null) {
ProviderInfo providerInfo = lastProviderInfo;
ClientTransport lastTransport = connectionHolder.getAvailableClientTransport(providerInfo);
if (lastTransport != null && lastTransport.isAvailable()) {
checkAlias(providerInfo, message);
return providerInfo;
}
}
}
// 原始服务列表数据 --> 路由结果
List<ProviderInfo> providerInfos = routerChain.route(message, null);
//保存一下原始地址,为了打印
List<ProviderInfo> orginalProviderInfos = new ArrayList<ProviderInfo>(providerInfos);
if (CommonUtils.isEmpty(providerInfos)) {
throw noAvailableProviderException(message.getTargetServiceUniqueName());
}
// 这个主要是重试的时候将异常的调用剔除掉,后面那个总数大于已调用数的条件是为了防止剔除后没有provider了
if (CommonUtils.isNotEmpty(invokedProviderInfos) && providerInfos.size() > invokedProviderInfos.size()) {
providerInfos.removeAll(invokedProviderInfos);// 已经调用异常的本次不再重试
}
String targetIP = null;
ProviderInfo providerInfo;
RpcInternalContext context = RpcInternalContext.peekContext();
if (context != null) {
targetIP = (String) RpcInternalContext.getContext().getAttachment(RpcConstants.HIDDEN_KEY_PINPOINT);
}
if (StringUtils.isNotBlank(targetIP)) {
// 如果指定了调用地址
providerInfo = selectPinpointProvider(targetIP, providerInfos);
if (providerInfo == null) {
// 指定的不存在
throw unavailableProviderException(message.getTargetServiceUniqueName(), targetIP);
}
ClientTransport clientTransport = selectByProvider(message, providerInfo);
if (clientTransport == null) {
// 指定的不存在或已死,抛出异常
throw unavailableProviderException(message.getTargetServiceUniqueName(), targetIP);
}
return providerInfo;
} else {
do {
// 再进行负载均衡筛选,前面在发布consumerConfig的时候有初始化loadBalancer,默认使用的random
providerInfo = loadBalancer.select(message, providerInfos);
ClientTransport transport = selectByProvider(message, providerInfo);
if (transport != null) {
return providerInfo;
}
providerInfos.remove(providerInfo);
} while (!providerInfos.isEmpty());
}
throw unavailableProviderException(message.getTargetServiceUniqueName(),
convertProviders2Urls(orginalProviderInfos));
}
对于loadBalancer.select(message, providerInfos);我们点进去是AbstractLoadBalancer#select
@Override
public ProviderInfo select(SofaRequest request, List<ProviderInfo> providerInfos) throws SofaRpcException {
if (providerInfos.size() == 0) {
throw noAvailableProviderException(request.getTargetServiceUniqueName());
}
// 如果只有一个provider就会直接使用这个
if (providerInfos.size() == 1) {
return providerInfos.get(0);
} else {
// 否则调用子类的筛选方法,我们以random为例,也就是RandomLoadBalancer#doSelect
return doSelect(request, providerInfos);
}
}
@Override
public ProviderInfo doSelect(SofaRequest invocation, List<ProviderInfo> providerInfos) {
ProviderInfo providerInfo = null;
int size = providerInfos.size(); // 总个数
int totalWeight = 0; // 总权重
boolean isWeightSame = true; // 权重是否都一样
for (int i = 0; i < size; i++) {
// 这里的权重数据来自于provider发布的时候的设置,有一个weight属性可以设置
int weight = getWeight(providerInfos.get(i));
totalWeight += weight; // 累计总权重
if (isWeightSame && i > 0 && weight != getWeight(providerInfos.get(i - 1))) {
isWeightSame = false; // 计算所有权重是否一样
}
}
if (totalWeight > 0 && !isWeightSame) {
// 如果权重不相同且权重大于0则按总权重数随机
int offset = random.nextInt(totalWeight);
// 并确定随机值落在哪个片断上
for (int i = 0; i < size; i++) {
// 使用总权重随机取值,然后落区间,那个减了之后小于0则落在哪个区间就直接取它既可
offset -= getWeight(providerInfos.get(i));
if (offset < 0) {
providerInfo = providerInfos.get(i);
break;
}
}
} else {
// 如果权重相同或权重为0则均等随机
providerInfo = providerInfos.get(random.nextInt(size));
}
return providerInfo;
}
然后就讲到下一行了 ClientTransport transport = selectByProvider(message, providerInfo);
/**
* 得到provider得到连接
*
* @param message 调用对象
* @param providerInfo 指定Provider
* @return 一个可用的transport或者null
*/
protected ClientTransport selectByProvider(SofaRequest message, ProviderInfo providerInfo) {
// 这里面就是前面维护的存活列表,亚健康列表和不可用列表或者第一次初始化下列表
ClientTransport transport = connectionHolder.getAvailableClientTransport(providerInfo);
if (transport != null) {
// 获得服务提供者的客户端传输者
if (transport.isAvailable()) {
// 将这个provider设置为最后一个调用的provider
lastProviderInfo = providerInfo;
checkAlias(providerInfo, message); //检查分组
return transport;
} else {
connectionHolder.setUnavailable(providerInfo, transport);
}
}
return null;
}
这里讲完了之后我们的负载均衡就讲完了,贴下前面的源码:
public SofaResponse doInvoke(SofaRequest request) throws SofaRpcException {
String methodName = request.getMethodName();
int retries = consumerConfig.getMethodRetries(methodName);
int time = 0;
SofaRpcException throwable = null;// 异常日志
List<ProviderInfo> invokedProviderInfos = new ArrayList<ProviderInfo>(retries + 1);
do {
ProviderInfo providerInfo = select(request, invokedProviderInfos);
try {
SofaResponse response = filterChain(providerInfo, request);
if (response != null) {
if (throwable != null) {
if (LOGGER.isWarnEnabled(consumerConfig.getAppName())) {
LOGGER.warnWithApp(consumerConfig.getAppName(),
LogCodes.getLog(LogCodes.WARN_SUCCESS_BY_RETRY,
throwable.getClass() + ":" + throwable.getMessage(),
invokedProviderInfos));
}
}
return response;
} else {
throwable = new SofaRpcException(RpcErrorType.CLIENT_UNDECLARED_ERROR,
"Failed to call " + request.getInterfaceName() + "." + methodName
+ " on remote server " + providerInfo + ", return null");
time++;
}
} catch (SofaRpcException e) { // 服务端异常+ 超时异常 才发起rpc异常重试
if (e.getErrorType() == RpcErrorType.SERVER_BUSY
|| e.getErrorType() == RpcErrorType.CLIENT_TIMEOUT) {
throwable = e;
time++;
} else {
throw e;
}
} catch (Exception e) { // 其它异常不重试
throw new SofaRpcException(RpcErrorType.CLIENT_UNDECLARED_ERROR,
"Failed to call " + request.getInterfaceName() + "." + request.getMethodName()
+ " on remote server: " + providerInfo + ", cause by unknown exception: "
+ e.getClass().getName() + ", message is: " + e.getMessage(), e);
} finally {
if (RpcInternalContext.isAttachmentEnable()) {
RpcInternalContext.getContext().setAttachment(RpcConstants.INTERNAL_KEY_INVOKE_TIMES,
time + 1); // 重试次数
}
}
invokedProviderInfos.add(providerInfo);
} while (time <= retries);
throw throwable;
}
从上面贴的源码可以看出,我们ProviderInfo providerInfo = select(request, invokedProviderInfos);已经分析完毕,只剩最后一步
SofaResponse response = filterChain(providerInfo, request);这里就是一个过滤器链的调用,我们挑一个发送请求的讲一下,ConsumerInvoker#invoke,实际上也没啥讲的
public SofaResponse invoke(SofaRequest sofaRequest) throws SofaRpcException {
// 设置下服务器应用
ProviderInfo providerInfo = RpcInternalContext.getContext().getProviderInfo();
String appName = providerInfo.getStaticAttr(ProviderInfoAttrs.ATTR_APP_NAME);
if (StringUtils.isNotEmpty(appName)) {
sofaRequest.setTargetAppName(appName);
}
// 目前只是通过client发送给服务端
return consumerBootstrap.getCluster().sendMsg(providerInfo, sofaRequest);
}
以上就是负载均衡使用的上下文全部解析,我们来总结下:
1、根据路由寻址找到所有的providers
2、筛选provider
3、获取clientTransport(发送使用客户端)
4、执行filterInovker调用链