加载机制
provider端
//org.apache.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol方法中
Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
//org.apache.dubbo.qos.protocol.QosProtocolWrapper#export
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (UrlUtils.isRegistry(invoker.getUrl())) {
startQosServer(invoker.getUrl());
return protocol.export(invoker);
}
return protocol.export(invoker);
}
//org.apache.dubbo.rpc.cluster.filter.ProtocolFilterWrapper#export
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (UrlUtils.isRegistry(invoker.getUrl())) {
return protocol.export(invoker);
}
//调用builder.buildInvokerChain方法 (SERVICE_FILTER_KEY = "service.filter",PROVIDER = "provider")
return protocol.export(builder.buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER));
}
comsumer端
//org.apache.dubbo.config.ReferenceConfig#createProxy
REF_PROTOCOL.refer(interfaceClass, urls.get(0));
//org.apache.dubbo.qos.protocol.QosProtocolWrapper#refer
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
if (UrlUtils.isRegistry(url)) {
startQosServer(url);
//进入此方法
return protocol.refer(type, url);
}
return protocol.refer(type, url);
}
//org.apache.dubbo.rpc.cluster.filter.ProtocolFilterWrapper#refer
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
if (UrlUtils.isRegistry(url)) {
return protocol.refer(type, url);
}
//调用builder.buildInvokerChain方法 (//REFERENCE_FILTER_KEY = "reference.filter",CONSUMER = "consumer" )
return builder.buildInvokerChain(protocol.refer(type, url), REFERENCE_FILTER_KEY, CommonConstants.CONSUMER);
}
默认filter
provider端:EchoFilter->ClassLoaderFilter->GenericFilter->ContextFilter->TraceFilter->TimeoutFilter->MonitorFilter->ExceptionFilter
consumer端:ConsumerContextFilter->FutureFilter->MonitorFilter
在org.apache.dubbo.rpc.filter包下面
用于调用信息进行日志打印
org.apache.dubbo.rpc.filter.AccessLogFilter
//org.apache.dubbo.rpc.filter.AccessLogFilter
//ACCESS_LOG_KEY = "accesslog" 我们需要配置下accesslog的参数值 即:日志打印的文件路径
@Activate(group = PROVIDER, value = ACCESS_LOG_KEY)
public class AccessLogFilter implements Filter {
//内部实现流程
//1、构造函数中创建定时任务,定时调用写日志文件方法
//2、如果有请求过来,调用invoke方法,以日志路径为key、请求信息为value集合 保存在 Map<String,Set<AccessLogData>>中
//3、如果定时任务触发执行,判断value值是否为空,不为空则写入日志文件中
}
配置示例:
<dubbo:service id="serviceDemo" accesslog="/Users/xxx/Downloads/log/dubbo_log.txt" interface="com.jiangzheng.course.dubbo.api.service.ServiceDemo" ref="iServiceDemo"/>
结果示例:
文件内容如下,包含请求类 及 方法 和 请求参数
[2022-03-22 20:49:06] 192.168.124.2:53880 -> 192.168.124.2:29014 - com.jiangzheng.course.dubbo.api.service.ServiceDemo getSelf(java.lang.String) [“fhaskfhjkas”]
org.apache.dubbo.rpc.filter.ExecuteLimitFilter
服务端接口限制限流的具体执行逻辑就是在ExecuteLimitFilter中,因为服务端不需要考虑重试等待逻辑,一旦当前执行的线程数量大于指定数量,就直接返回失败了
@Activate(group = CommonConstants.PROVIDER, value = EXECUTES_KEY) //EXECUTES_KEY = "executes"
public class ExecuteLimitFilter implements Filter, Filter.Listener {
private static final String EXECUTE_LIMIT_FILTER_START_TIME = "execute_limit_filter_start_time";
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
//获取url
URL url = invoker.getUrl();
//获取调用的方法名
String methodName = invocation.getMethodName();
//获取允许最大的请求量
int max = url.getMethodParameter(methodName, EXECUTES_KEY, 0);
//判断是否达到了最大值,未达到的话 将方法和接口状态值都加一 并返回 true,否则返回false
if (!RpcStatus.beginCount(url, methodName, max)) {
throw new RpcException(RpcException.LIMIT_EXCEEDED_EXCEPTION,
"Failed to invoke method " + invocation.getMethodName() + " in provider " +
url + ", cause: The service using threads greater than <dubbo:service executes=\"" + max +
"\" /> limited.");
}
//用于计算执行时间,用于请求时间的统计
invocation.put(EXECUTE_LIMIT_FILTER_START_TIME, System.currentTimeMillis());
try {
//执行调用
return invoker.invoke(invocation);
} catch (Throwable t) {
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new RpcException("unexpected exception when ExecuteLimitFilter", t);
}
}
}
@Override
public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
RpcStatus.endCount(invoker.getUrl(), invocation.getMethodName(), getElapsed(invocation), true);
}
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
if (t instanceof RpcException) {
RpcException rpcException = (RpcException) t;
if (rpcException.isLimitExceed()) {
return;
}
}
RpcStatus.endCount(invoker.getUrl(), invocation.getMethodName(), getElapsed(invocation), false);
}
private long getElapsed(Invocation invocation) {
Object beginTime = invocation.get(EXECUTE_LIMIT_FILTER_START_TIME);
return beginTime != null ? System.currentTimeMillis() - (Long) beginTime : 0;
}
}
org.apache.dubbo.rpc.filter.ClassLoaderFilter
ClassLoaderFilter 里 ,ClassLoaderFilter 代码好理解,将当前线程的ClassLoader切换成服务调用接口的ClassLoader,服务调用完毕再切换回来, 用于打破双亲委派 调用一些非同一类加载器加载的类方法
@Activate(group = CommonConstants.PROVIDER, order = -30000)
public class ClassLoaderFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
//获取原始的类加载器
ClassLoader ocl = Thread.currentThread().getContextClassLoader();
//切换为invoker相关的类加载器
Thread.currentThread().setContextClassLoader(invoker.getInterface().getClassLoader());
try {
//执行调用
return invoker.invoke(invocation);
} finally {
//重新切换回来
Thread.currentThread().setContextClassLoader(ocl);
}
}
}
org.apache.dubbo.rpc.filter.ConsumerContextFilter
其实简单来看这个Filter的话是十分简单,它又是怎么将客户端设置的隐式参数传递给服务端呢?载体就是Invocation对象,在客户端调用Invoker.invoke方法时候,会去取当前状态记录器RpcContext中的attachments属性,然后设置到RpcInvocation对象中,在RpcInvocation传递到provider的时候会通过另外一个过滤器ContextFilter将RpcInvocation对象重新设置回RpcContext中供服务端逻辑重新获取隐式参数。这就是为什么RpcContext只能记录一次请求的状态信息,因为在第二次调用的时候参数已经被新的RpcInvocation覆盖掉,第一次的请求信息对于第二次执行是不可见的
@Activate(group = CONSUMER, order = -10000)
public class ConsumerContextFilter implements ClusterFilter, ClusterFilter.Listener {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
RpcContext context = RpcContext.getContext();
//在当前的RpcContext中记录本地调用的一次状态信息
context.setInvocation(invocation)
.setLocalAddress(NetUtils.getLocalHost(), 0)
.setAttachment(REMOTE_APPLICATION_KEY, invoker.getUrl().getApplication());
if (invocation instanceof RpcInvocation) {
((RpcInvocation) invocation).setInvoker(invoker);
}
Map<String, Object> contextAttachments = RpcContext.getContext().getObjectAttachments();
if (CollectionUtils.isNotEmptyMap(contextAttachments)) {
((RpcInvocation) invocation).addObjectAttachments(contextAttachments);
}
// pass default timeout set by end user (ReferenceConfig)
Object countDown = context.get(TIME_COUNTDOWN_KEY);
if (countDown != null) {
TimeoutCountDown timeoutCountDown = (TimeoutCountDown) countDown;
if (timeoutCountDown.isExpired()) {
return AsyncRpcResult.newDefaultAsyncResult(new RpcException(RpcException.TIMEOUT_TERMINATE,
"No time left for making the following call: " + invocation.getServiceName() + "."
+ invocation.getMethodName() + ", terminate directly."), invocation);
}
}
try {
RpcContext.removeServerContext();
return invoker.invoke(invocation);
} finally {
RpcContext.removeContext();
}
}
@Override
public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
// pass attachments to result
RpcContext.getServerContext().setObjectAttachments(appResponse.getObjectAttachments());
}
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
}
}
org.apache.dubbo.rpc.filter.ContextFilter
ContextFilter和ConsumerContextFilter是结合使用的
@Activate(group = PROVIDER, order = -10000)
public class ContextFilter implements Filter, Filter.Listener {
private static final String TAG_KEY = "dubbo.tag";
private static final Set<String> UNLOADING_KEYS;
static {
UNLOADING_KEYS = new HashSet<>(128);
UNLOADING_KEYS.add(PATH_KEY);
UNLOADING_KEYS.add(INTERFACE_KEY);
UNLOADING_KEYS.add(GROUP_KEY);
UNLOADING_KEYS.add(VERSION_KEY);
UNLOADING_KEYS.add(DUBBO_VERSION_KEY);
UNLOADING_KEYS.add(TOKEN_KEY);
UNLOADING_KEYS.add(TIMEOUT_KEY);
UNLOADING_KEYS.add(TIMEOUT_ATTACHMENT_KEY);
// Remove async property to avoid being passed to the following invoke chain.
UNLOADING_KEYS.add(ASYNC_KEY);
UNLOADING_KEYS.add(TAG_KEY);
UNLOADING_KEYS.add(FORCE_USE_TAG);
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
Map<String, Object> attachments = invocation.getObjectAttachments();
if (attachments != null) {
//隐式参数重剔除一些核心消息
Map<String, Object> newAttach = new HashMap<>(attachments.size());
for (Map.Entry<String, Object> entry : attachments.entrySet()) {
String key = entry.getKey();
if (!UNLOADING_KEYS.contains(key)) {
newAttach.put(key, entry.getValue());
}
}
attachments = newAttach;
}
//这里又重新将invocation和attachments信息设置到RpcContext,这里设置以后provider的代码就可以获取到consumer端传递的一些隐式参数了
RpcContext context = RpcContext.getContext();
context.setInvoker(invoker)
.setInvocation(invocation)
// .setAttachments(attachments) // merged from dubbox
.setLocalAddress(invoker.getUrl().getHost(), invoker.getUrl().getPort());
String remoteApplication = (String) invocation.getAttachment(REMOTE_APPLICATION_KEY);
if (StringUtils.isNotEmpty(remoteApplication)) {
context.setRemoteApplicationName(remoteApplication);
} else {
context.setRemoteApplicationName((String) context.getAttachment(REMOTE_APPLICATION_KEY));
}
long timeout = RpcUtils.getTimeout(invocation, -1);
if (timeout != -1) {
context.set(TIME_COUNTDOWN_KEY, TimeoutCountDown.newCountDown(timeout, TimeUnit.MILLISECONDS));
}
// merged from dubbox
// we may already added some attachments into RpcContext before this filter (e.g. in rest protocol)
if (attachments != null) {
if (context.getObjectAttachments() != null) {
context.getObjectAttachments().putAll(attachments);
} else {
context.setObjectAttachments(attachments);
}
}
if (invocation instanceof RpcInvocation) {
((RpcInvocation) invocation).setInvoker(invoker);
}
try {
context.clearAfterEachInvoke(false);
return invoker.invoke(invocation);
} finally {
context.clearAfterEachInvoke(true);
// IMPORTANT! For async scenario, we must remove context from current thread, so we always create a new RpcContext for the next invoke for the same thread.
RpcContext.removeContext(true);
RpcContext.removeServerContext();
}
}
@Override
public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
// pass attachments to result
appResponse.addObjectAttachments(RpcContext.getServerContext().getObjectAttachments());
}
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
}
}
org.apache.dubbo.rpc.filter.ActiveLimitFilter
当配置了actives并且值不为0的时候触发, ActiveLimitFilte主要用于限制同一个客户端对于一个服务端方法的并发调用量。(客户端限流)
@Activate(group = CONSUMER, value = ACTIVES_KEY)
public class ActiveLimitFilter implements Filter, Filter.Listener {
private static final String ACTIVELIMIT_FILTER_START_TIME = "activelimit_filter_start_time";
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
URL url = invoker.getUrl();
String methodName = invocation.getMethodName();
int max = invoker.getUrl().getMethodParameter(methodName, ACTIVES_KEY, 0);
//主要记录每台机器针对某个方法的并发数量
final RpcStatus rpcStatus = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName());
if (!RpcStatus.beginCount(url, methodName, max)) {
long timeout = invoker.getUrl().getMethodParameter(invocation.getMethodName(), TIMEOUT_KEY, 0);
long start = System.currentTimeMillis();
long remain = timeout;
synchronized (rpcStatus) {
//调用开始和结束后增减并发数量
//这个while循环是必要的,因为在一次wait结束后,可能线程调用已经结束了,腾出来consumer的空间
while (!RpcStatus.beginCount(url, methodName, max)) {
try {
rpcStatus.wait(remain);
} catch (InterruptedException e) {
// ignore
}
//如果wait方法被中断的话,remain这时候有可能大于0
//如果其中一个线程运行结束自后调用notify方法的话,也有可能remain大于0
long elapsed = System.currentTimeMillis() - start;
remain = timeout - elapsed;
if (remain <= 0) {
throw new RpcException(RpcException.LIMIT_EXCEEDED_EXCEPTION,
"Waiting concurrent invoke timeout in client-side for service: " +
invoker.getInterface().getName() + ", method: " + invocation.getMethodName() +
", elapsed: " + elapsed + ", timeout: " + timeout + ". concurrent invokes: " +
rpcStatus.getActive() + ". max concurrent invoke limit: " + max);
}
}
}
}
invocation.put(ACTIVELIMIT_FILTER_START_TIME, System.currentTimeMillis());
return invoker.invoke(invocation);
}
@Override
public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
String methodName = invocation.getMethodName();
URL url = invoker.getUrl();
int max = invoker.getUrl().getMethodParameter(methodName, ACTIVES_KEY, 0);
RpcStatus.endCount(url, methodName, getElapsed(invocation), true);
//这里很关键,因为一个调用完成后要通知正在等待执行的队列
notifyFinish(RpcStatus.getStatus(url, methodName), max);
}
@Override
public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
String methodName = invocation.getMethodName();
URL url = invoker.getUrl();
int max = invoker.getUrl().getMethodParameter(methodName, ACTIVES_KEY, 0);
if (t instanceof RpcException) {
RpcException rpcException = (RpcException) t;
if (rpcException.isLimitExceed()) {
return;
}
}
RpcStatus.endCount(url, methodName, getElapsed(invocation), false);
//这里很关键,因为一个调用完成后要通知正在等待执行的队列
notifyFinish(RpcStatus.getStatus(url, methodName), max);
}
private long getElapsed(Invocation invocation) {
Object beginTime = invocation.get(ACTIVELIMIT_FILTER_START_TIME);
return beginTime != null ? System.currentTimeMillis() - (Long) beginTime : 0;
}
private void notifyFinish(final RpcStatus rpcStatus, int max) {
if (max > 0) {
synchronized (rpcStatus) {
rpcStatus.notifyAll();
}
}
}
}