Filter:
filter过滤器动态拦截请求(request)或响应(response)以转换或使用请求或响应中包含的信息。同时对于filter过滤器不仅适合消费端而且还适合服务提供端。我们可以自定义在什么情况下去使用filter过滤器
@Activate(group = PROVIDER)
public class AccessLogFilter implements Filter {}
如上述代码就明确的定义了filter类所使用的场景
Activate注解则表示在哪种场景下去使用filter,Activate注解的属性有group,value ,order来更加精细的控制激活条件
dubbo:
consumer:
filter: "-accesslog,-tps"
@DubboReference(filter="-accesslog,-tps")
private DemoService demoService;
可以全局关闭加载filter,也可以精确到某个服务中不加载filter,当参数改为accesslog,tps的时候则表示的是可以全局自动使用filter,所有 rpc 调用均启用 filter
Filter定义:
public interface BaseFilter {
/**
* Always call invoker.invoke() in the implementation to hand over the request to the next filter node.
*/
Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;
/**
* This callback listener applies to both synchronous and asynchronous calls, please put logics that need to be executed
* on return of rpc result in onResponse or onError respectively based on it is normal return or exception return.
* <p>
* There's something that needs to pay attention on legacy synchronous style filer refactor, the thing is, try to move logics
* previously defined in the 'finally block' to both onResponse and onError.
*/
interface Listener {
/**
* This method will only be called on successful remote rpc execution, that means, the service in on remote received
* the request and the result (normal or exceptional) returned successfully.
*/
void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation);
/**
* This method will be called on detection of framework exceptions, for example, TimeoutException, NetworkException
* Exception raised in Filters, etc.
*/
void onError(Throwable t, Invoker<?> invoker, Invocation invocation);
}
}
基于以上 BaseFilter 定义,Dubbo 定义了两个 SPI 接口:ClusterFilter 与 Filter。这两个 SPI 实现能实现的效果基本是一致的,之所以定义两个主要是出于性能优化考虑,建议用户关注 Filter SPI 即可,仅在有严苛性能需求的情况下(如集群 provider 提供者实例数量庞大)才关注 ClusterFilter。ClusterFilter相比Filter提前作用
超时时间:
对于RPC调用可能对方服务已经出现问题了如果调用方一直等待就会造成资源的浪费(比如占用大量的线程池等)所以我们可以设置超时时间这样当超过这个时间之后就会自动释放此次调用
//全局
dubbo:
provider:
timeout: 5000
//指定服务消费端
@DubboReference(timeout=5000)
private DemoService demoService;
//指定服务提供端
@DubboService(timeout=5000)
public class DemoServiceImpl implements DemoService{}
//指定方法消费端
@DubboReference(methods = {@Method(name = "sayHello", timeout = 5000)})
private DemoService demoService;
//指定方法服务端
@DubboService(methods = {@Method(name = "sayHello", timeout = 5000)})
public class DemoServiceImpl implements DemoService{}
优先级:
- 方法级消费端配置(最高)
- 服务级消费端配置
- 方法级提供端配置
- 服务级提供端配置
- 全局默认配置(最低)
Deadline 机制
在多级调用的时候假如服务A调用服务B出现了超时那么A就会自动返回超时方案,那么此时如果服务B还在等待服务C的返回就会造成资源的浪费,特别是服务C后续如果还有调用端
因此Dubbo中采用了deadline机制,通过在调用链路中传递 deadline(deadline初始值等于超时时间,随着时间流逝而减少)可以确保调用链路只在有效期内执行,deadline 消耗殆尽之后,调用链路中其他尚未执行的任务将被取消。相当于A的超时时间辐射到整个调用链路中
默认情况下deadline是关闭的
//全局
dubbo:
provider:
timeout: 5000
parameters.enable-timeout-countdown: true
//服务
@DubboReference(timeout=5000, parameters={"enable-timeout-countdown", "true"})
private DemoService demoService;
传递隐式参数:
当我们想要传递与业务无关的参数的时候可以通过 RpcContext
上的 setAttachment
和 getAttachment
在服务消费方和提供方之间进行参数的隐式传递。
隐式参数传递支持以下两个方向:
- 从消费方到提供方,也就是在请求发起时,在方法参数之外通过 attachment 传递附加参数。
- 从提供方到消费方,也就是在响应结果返回时,在响应结果之外通过 attachment 传递附加参数。
理解隐式参数传递的最直接方式 http header,它的工作方式与 http header 完全一致,在 GET 或 POST 请求体之外可以传递任意多个 header 参数。在实现原理上,对于不同的协议,attachment 的实现方式略有不同:
- 对于 triple 协议,attachment 会转换为标准的 http header 进行传输。
- 对于 dubbo 协议,attachment 是编码在协议体的固定位置进行传输。
Dubbo 中的 RpcContext 是一个 ThreadLocal 的临时状态记录器,当接收到 RPC 请求,或发起 RPC 请求时,RpcContext 的状态都会变化。比如:A 调 B,B 调 C,则 B 机器上,在 B 调 C 之前,RpcContext 记录的是 A 和 B 的信息,在 B 调 C 之后,RpcContext 记录的是 B 和 C 的信息。
//消费端
RpcContext.getClientAttachment().setAttachment("index", "1"); // 隐式传参,后面的远程调用都会隐式将这些参数发送到服务器端,类似cookie,比如用于框架集成
xxxService.xxx(); // 远程调用
// ...
//服务端
xxxService.xxx(); // 远程调用
String result = RpcContext.getServerContext().getAttachment("result");
// ...
public class XxxServiceImpl implements XxxService {
public void xxx() {
// 获取客户端隐式传入的参数,比如用于框架集成
String index = RpcContext.getServerAttachment().getAttachment("index");
}
}
请注意!setAttachment
设置的 KV 对,在完成下面一次远程调用会被清空,即多次远程调用要多次设置!这一点与 Dubbo2 中的行为是不一致的!
比如,对于 Dubbo2 而言,在 A 端设置的参数,调用 B 以后,如果 B 继续调用了 C,原来在 A 中设置的参数也会被带到 C 端过去(造成参数污染的问题)。对于 Dubbo3,B 调用 C 时的上下文是干净的,不会包含最开始在 A 中设置的参数。
安全策略:
权限控制:
通过令牌验证在注册中心控制权限,以决定要不要下发令牌给消费者, 可以防止消费者绕过注册中心访问提供者, 另外通过注册中心可灵活改变授权方式,而不需修改或升级提供者。
<!--随机token令牌,使用UUID生成-->
<dubbo:provider token="true" />
<!--固定token令牌,相当于密码-->
<dubbo:provider token="123456" />
<!--随机token令牌,使用UUID生成-->
<dubbo:service interface="com.foo.BarService" token="true" />
<!--固定token令牌,相当于密码-->
<dubbo:service interface="com.foo.BarService" token="123456" />
服务鉴权:
类似支付之类的对安全性敏感的业务可能会有限制匿名调用的需求。在加固安全性方面,2.7.5 引入了基于 AK/SK 机制的认证鉴权机制,并且引入了鉴权服务中心,主要原理是消费端在请求需要鉴权的服务时,会通过 SK、请求元数据、时间戳、参数等信息来生成对应的请求签名,通过 Dubbo 的 Attahcment 机制携带到对端进行验签,验签通过才进行业务逻辑处理。如下图所示