SOFARPC 项目中运用了多种设计模式
1. 工厂模式(Factory Pattern)
工厂模式是一种创建对象的设计模式,它将对象的创建和使用分离。在项目中,ProviderConfig
和 ConsumerConfig
类在一定程度上体现了工厂模式的思想,它们负责创建服务提供者和消费者的配置对象。
// 创建服务提供者配置
ProviderConfig<SampleService> serviceBeanC = new ProviderConfig<SampleService>();
serviceBeanC.setInterfaceId(SampleService.class.getName());
serviceBeanC.setApplication(new ApplicationConfig().setAppName("CCC"));
serviceBeanC.setUniqueId("C5");
serviceBeanC.setRef(refC);
serviceBeanC.setServer(serverConfig);
serviceBeanC.setRegister(false);
serviceBeanC.export();
// 创建服务消费者配置
ConsumerConfig referenceBeanC = new ConsumerConfig();
referenceBeanC.setApplication(new ApplicationConfig().setAppName("BBB"));
referenceBeanC.setInterfaceId(SampleService.class.getName());
referenceBeanC.setUniqueId("C5");
referenceBeanC.setDirectUrl("127.0.0.1:12299");
referenceBeanC.setTimeout(1000);
SampleService sampleServiceC = (SampleService) referenceBeanC.refer();
解析:ProviderConfig
和 ConsumerConfig
类封装了服务提供者和消费者的创建过程,通过一系列的 set
方法设置配置信息,最后调用 export()
或 refer()
方法创建和启动服务。这样,使用者不需要关心具体的创建细节,只需要使用配置类提供的接口即可。
2. 单例模式(Singleton Pattern)
DynamicConfigManagerFactory
类虽然不是严格意义上的单例(因为它不限制自己创建多个实例),但它对 DynamicConfigManager
实例的管理采用了类似单例的思想,确保相同别名的 DynamicConfigManager
只创建一次。
public class DynamicConfigManagerFactory {
/**
* 保存全部的配置和注册中心实例
*/
private final static ConcurrentMap<String, DynamicConfigManager> ALL_DYNAMICS = new ConcurrentHashMap<String, DynamicConfigManager>();
/**
* 类锁
*/
private final static Lock classLock = new ReentrantLock();
/**
* 得到动态配置管理器
*
* @param alias 别名
* @return DynamicManager 实现
*/
public static DynamicConfigManager getDynamicManager(String appName, String alias) {
classLock.lock();
try {
if (ALL_DYNAMICS.size() > 3) { // 超过3次 是不是配错了?
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Size of dynamic manager is greater than 3, Please check it!");
}
}
// 注意:RegistryConfig重写了equals方法,如果多个RegistryConfig属性一样,则认为是一个对象
DynamicConfigManager registry = ALL_DYNAMICS.get(alias);
if (registry == null) {
ExtensionClass<DynamicConfigManager> ext = ExtensionLoaderFactory.getExtensionLoader(
DynamicConfigManager.class)
.getExtensionClass(alias);
if (ext == null) {
throw new SofaRpcRuntimeException(LogCodes.getLog(LogCodes.ERROR_LOAD_EXT, "DynamicConfigManager",
alias));
}
registry = ext.getExtInstance(new Class[] { String.class }, new Object[] { appName });
ALL_DYNAMICS.put(alias, registry);
}
return registry;
} catch (Throwable e) {
throw new SofaRpcRuntimeException(LogCodes.getLog(LogCodes.ERROR_LOAD_EXT, "DynamicConfigManager", alias),
e);
} finally {
classLock.unlock();
}
}
}
- 全局唯一实例管理:
ALL_DYNAMICS
是一个ConcurrentMap
,用于存储所有创建的DynamicConfigManager
实例。通过别名(alias
)作为键,确保相同别名的DynamicConfigManager
只创建一次。
- 线程安全的创建方式:
- 使用
ReentrantLock
(classLock
)来保证在多线程环境下,同一时间只有一个线程可以创建新的DynamicConfigManager
实例。
- 使用
3. 策略模式(Strategy Pattern)
策略模式定义了一系列的算法,并将每个算法封装起来,使它们可以相互替换。在项目中,MeasureStrategy
接口和其实现类 ServiceHorizontalMeasureStrategy
体现了策略模式。
MeasureStrategy measureStrategy = new ServiceHorizontalMeasureStrategy();
MeasureResult measureResult = measureStrategy.measure(measureModel);
解析:MeasureStrategy
是一个策略接口,定义了度量的方法 measure()
。ServiceHorizontalMeasureStrategy
是该接口的具体实现类,实现了具体的度量算法。通过使用策略模式,可以根据不同的需求选择不同的度量策略,而不需要修改调用代码。
4. 责任链模式(Chain of Responsibility Pattern)
责任链模式将请求的发送者和接收者解耦,使多个对象都有机会处理这个请求。在项目中,RouterChain
类体现了责任链模式。
public class RouterChain {
private final List<Router> routers;
public RouterChain(List<Router> actualRouters, ConsumerBootstrap consumerBootstrap) {
this.routers = new LinkedList<Router>();
if (CommonUtils.isNotEmpty(actualRouters)) {
for (Router router : actualRouters) {
if (router.needToLoad(consumerBootstrap)) {
router.init(consumerBootstrap);
routers.add(router);
}
}
}
}
public List<ProviderInfo> route(SofaRequest request, List<ProviderInfo> providerInfos) {
for (Router router : routers) {
providerInfos = router.route(request, providerInfos);
}
return providerInfos;
}
}
解析:RouterChain
类包含一个 Router
列表,每个 Router
都有机会对请求进行处理。route()
方法依次调用列表中的每个 Router
的 route()
方法,直到请求被处理完毕或到达责任链的末尾。这种模式使得请求的处理过程可以灵活配置,并且可以动态地添加或删除处理节点。
5. 观察者模式(Observer Pattern)
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听一个主题对象。在项目中,ProviderInfoListener
接口和相关的实现类体现了观察者模式。
public abstract class Cluster implements Invoker, ProviderInfoListener, Initializable, Destroyable {
// ...
}
解析:ProviderInfoListener
是一个观察者接口,当服务提供者信息发生变化时,实现该接口的类(如 Cluster
类)会收到通知并进行相应的处理。这种模式使得系统的各个组件之间可以松耦合,提高了系统的可维护性和可扩展性。
6. 装饰器模式
ClientProxyInvoker
类中的 invoke
方法对请求和响应的处理也有装饰器模式的影子:
@ThreadSafe
public class ClientProxyInvoker implements Invoker {
// ... 其他代码 ...
@Override
public SofaResponse invoke(SofaRequest request) throws SofaRpcException {
SofaResponse response = null;
Throwable throwable = null;
try {
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();
}
}
protected void decorateRequest(SofaRequest request) {
// 暂时不支持隐式传参
String methodName = request.getMethodName();
// 将接口的<sofa:param />的配置复制到invocation
Map params = consumerConfig.getParameters();
if (params != null) {
request.addRequestProps(params);
}
// 将方法的<sofa:param />的配置复制到invocation
params = (Map) consumerConfig.getMethodConfigValue(methodName, RpcConstants.CONFIG_KEY_PARAMS);
if (params != null) {
request.addRequestProps(params);
}
}
protected void decorateResponse(SofaResponse response) {
// NOPMD
}
// ... 其他代码 ...
}
装饰器模式的体现
- 功能扩展:
invoke
方法在执行核心的cluster.invoke(request)
调用前后,对请求和响应进行了额外的处理。decorateRequest
方法为请求添加了额外的参数,decorateResponse
方法虽然当前为空,但可以扩展用于对响应进行额外处理。这些额外的处理就是在不改变SofaRequest
和SofaResponse
核心结构的基础上,为其添加了新的功能。 - 不改变原有结构:
SofaRequest
和SofaResponse
对象的基本结构没有被改变,只是在原有的基础上添加了新的属性或信息。例如,decorateRequest
方法通过request.addRequestProps(params)
为请求添加参数,并没有改变SofaRequest
类的定义。 - 动态添加功能:可以根据不同的条件或配置,动态地决定是否执行这些额外的处理。例如,通过
EventBus.isEnable(ClientStartInvokeEvent.class)
和EventBus.isEnable(ClientEndInvokeEvent.class)
来判断是否触发调用开始和结束事件。
7. 模板方法模式(Template Method Pattern)
模板方法模式定义了一个操作中的算法骨架,将一些步骤延迟到子类中实现。在 ClientTransport
类中可以看到模板方法模式的应用。ClientTransport
是一个抽象类,它定义了客户端传输的基本操作流程,其中一些方法是抽象的,需要子类去具体实现。
@Extensible(singleton = false)
public abstract class ClientTransport {
protected ClientTransportConfig transportConfig;
protected ClientTransport(ClientTransportConfig transportConfig) {
this.transportConfig = transportConfig;
}
// 抽象方法,需要子类实现
public abstract void connect();
public abstract void disconnect();
public abstract void destroy();
public abstract boolean isAvailable();
public abstract void setChannel(AbstractChannel channel);
public abstract AbstractChannel getChannel();
public abstract int currentRequests();
public abstract ResponseFuture asyncSend(SofaRequest message, int timeout) throws SofaRpcException;
public abstract SofaResponse syncSend(SofaRequest message, int timeout) throws SofaRpcException;
public abstract void oneWaySend(SofaRequest message, int timeout) throws SofaRpcException;
public abstract void receiveRpcResponse(SofaResponse response);
public abstract void handleRpcRequest(SofaRequest request);
public abstract InetSocketAddress remoteAddress();
public abstract InetSocketAddress localAddress();
// 具体方法,提供通用的逻辑
public ClientTransportConfig getConfig() {
return transportConfig;
}
}
解析:ClientTransport
类定义了客户端传输的通用流程,其中抽象方法如 connect()
、disconnect()
等需要子类去实现具体的连接、断开连接等操作。而具体方法 getConfig()
则提供了通用的获取配置的逻辑。子类只需要实现抽象方法,就可以完成特定的客户端传输功能,遵循了模板方法模式的设计思想。
8. 代理模式(Proxy Pattern)
代理模式为其他对象提供一种代理以控制对这个对象的访问。在 DefaultClientProxyInvoker
类中可以看到代理模式的应用。DefaultClientProxyInvoker
是客户端代理执行器,它代理了客户端的调用操作。
public class DefaultClientProxyInvoker extends ClientProxyInvoker {
protected String serviceName;
protected Byte serializeType;
public DefaultClientProxyInvoker(ConsumerBootstrap bootstrap) {
super(bootstrap);
cacheCommonData();
}
@Override
protected void decorateRequest(SofaRequest request) {
// 公共的设置
super.decorateRequest(request);
// 缓存是为了加快速度
request.setTargetServiceUniqueName(serviceName);
request.setSerializeType(serializeType == null ? 0 : serializeType);
// 其他请求装饰逻辑
}
@Override
protected void decorateResponse(SofaResponse response) {
// 公共的设置
super.decorateResponse(response);
// 上下文内转外
RpcInternalContext context = RpcInternalContext.getContext();
ResponseFuture future = context.getFuture();
RpcInvokeContext invokeCtx = null;
if (future != null) {
invokeCtx = RpcInvokeContext.getContext();
invokeCtx.setFuture(future);
}
// 其他响应装饰逻辑
}
// 其他方法
}
解析:DefaultClientProxyInvoker
类代理了客户端的调用操作,在 decorateRequest()
和 decorateResponse()
方法中对请求和响应进行了装饰,添加了一些额外的逻辑,如设置请求的目标服务名、序列化类型,处理上下文信息等。客户端通过 DefaultClientProxyInvoker
来间接调用服务,而不需要直接处理这些复杂的逻辑,体现了代理模式的思想。
9. 适配器模式(Adapter Pattern)
适配器模式将一个类的接口转换成客户希望的另外一个接口。在 TripleClientInvoker
类中,ClientStreamObserverAdapter
类可以看作是一个适配器,它将 SofaStreamObserver
接口适配成 StreamObserver
接口。
public class TripleClientInvoker implements TripleInvoker {
// ...
private SofaResponse genericBinaryStreamCall(SofaRequest sofaRequest, int timeout) {
SofaStreamObserver sofaStreamObserver = (SofaStreamObserver) sofaRequest.getMethodArgs()[0];
MethodDescriptor<Request, Response> methodDescriptor = getMethodDescriptor(sofaRequest);
ClientCall<Request, Response> call = channel.newCall(methodDescriptor, buildCustomCallOptions(sofaRequest, timeout));
StreamObserver<Request> observer = ClientCalls.asyncBidiStreamingCall(
call,
new ClientStreamObserverAdapter(
sofaStreamObserver,
sofaRequest.getSerializeType(),
ClassLoaderUtils.getCurrentClassLoader()
)
);
// ...
}
// ...
}
解析:ClientStreamObserverAdapter
类将 SofaStreamObserver
接口适配成 StreamObserver
接口,使得 SofaStreamObserver
可以与 gRPC 的 StreamObserver
进行交互。这样,TripleClientInvoker
可以使用 SofaStreamObserver
来处理流数据,同时又能与 gRPC 的 API 兼容,体现了适配器模式的应用。