Guarded Suspension 模式
考虑一个场景,当两个服务通过MQ进行交互时,服务A生产消息,服务B消费消息,两者为异步操作,我们如何来保证A消费到B返回的结果呢?
Guarded Suspension模式
所谓 Guarded Suspension,直译过来就是“保护性地暂停”。
Guarded Suspension模式的结构图如下,其中包含:
- 一个受保护的对象
GuardedObject
- 两个成员方法:
get(Predicate p)
和onChanged(T obj)
方法
GuardedObject
是管程的一个经典用法,将等待-通知机制展现得淋漓尽致。我们只需要检查条件变量的状态,来判断“受保护对象”是否能被访问即可。
核心实现:
- get(Predicate p) 通过条件变量的await()实现等待
- onChanged() 通过条件变量的signalAll()实现唤醒功能
class GuardedObject<T> {
// 受保护的对象
T obj;
final Lock lock = new ReentrantLock();
final Condition done = lock.newCondition();
final int timeout = 1;
// 获取受保护对象
T get(Predicate<T> p) {
lock.lock();
try {
//MESA管程推荐写法
while(!p.test(obj)) {
done.await(timeout,
TimeUnit.SECONDS);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
// 返回非空的受保护对象
return obj;
}
// 事件通知方法
void onChanged(T obj) {
lock.lock();
try {
this.obj = obj;
done.signalAll();
} finally {
lock.unlock();
}
}
}
扩展Guarded Suspension模式
我们来模拟消息消费场景,
//处理浏览器发来的请求
Respond handleWebReq() {
// 创建一消息
Message msg = new
Message("1", "{...}");
// 发送消息
send(msg);
//利用GuardedObject实现等待
GuardedObject<Message> go
=new GuardObjec<>();
Message r = go.get(
t->t != null);
}
void onMessage(Message msg) {
//如何找到匹配的go
GuardedObject<Message> go =
go.onChanged(msg);
}
服务A处理web请求,向MQ发送消息,等待消息反馈结果;服务B消费消息,向MQ反馈结果。
那么服务B消费消息后,如何通知服务A呢?我们需要找到Message对应的GuardedObject对象。
这里我们在GuardedObject
中维护一个Map,用以映射消息和受保护对象的关系。
class GuardedObject<T> {
// 受保护的对象
T obj;
final Lock lock = new ReentrantLock();
final Condition done = lock.newCondition();
final int timeout = 2;
//保存所有GuardedObject
final static Map<Object, GuardedObject> gos = new ConcurrentHashMap<>();
//静态方法创建GuardedObject
static <K> GuardedObject create(K key) {
GuardedObject go = new GuardedObject();
gos.put(key, go);
return go;
}
// 消费
static <K, T> void fireEvent(K key, T obj){
GuardedObject go = gos.remove(key);
if (go != null) {
go.onChanged(obj);
}
}
// 事件通知方法
void onChanged(T obj) {
lock.lock();
try {
this.obj = obj;
done.signalAll();
} finally {
lock.unlock();
}
}
// 获取受保护对象
T get(Predicate<T> p) {
lock.lock();
try {
//MESA管程推荐写法
while(!p.test(obj)){
done.await(timeout,
TimeUnit.SECONDS);
}
} catch(InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
//返回非空的受保护对象
return obj;
}
}
- create 创建 GuardedObject,并统一管理
- fireEvent 触发消费,改变条件变量状态
- get 获取消费结果
Guarded Suspension 模式本质上是一种等待唤醒机制的实现,只不过Guarded Suspension 模式将其规范化,通常我们会根据实际场景扩展使用。