SOFA RPC中设计模式的应用

SOFARPC 项目中运用了多种设计模式

1. 工厂模式(Factory Pattern)

工厂模式是一种创建对象的设计模式,它将对象的创建和使用分离。在项目中,ProviderConfigConsumerConfig 类在一定程度上体现了工厂模式的思想,它们负责创建服务提供者和消费者的配置对象。

// 创建服务提供者配置
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();

解析ProviderConfigConsumerConfig 类封装了服务提供者和消费者的创建过程,通过一系列的 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();
        }
    }
}
  1. 全局唯一实例管理
    • ALL_DYNAMICS 是一个 ConcurrentMap,用于存储所有创建的 DynamicConfigManager 实例。通过别名(alias)作为键,确保相同别名的 DynamicConfigManager 只创建一次。
  2. 线程安全的创建方式
    • 使用 ReentrantLockclassLock)来保证在多线程环境下,同一时间只有一个线程可以创建新的 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() 方法依次调用列表中的每个 Routerroute() 方法,直到请求被处理完毕或到达责任链的末尾。这种模式使得请求的处理过程可以灵活配置,并且可以动态地添加或删除处理节点。

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
    }

    // ... 其他代码 ...
}

装饰器模式的体现

  1. 功能扩展invoke 方法在执行核心的 cluster.invoke(request) 调用前后,对请求和响应进行了额外的处理。decorateRequest 方法为请求添加了额外的参数,decorateResponse 方法虽然当前为空,但可以扩展用于对响应进行额外处理。这些额外的处理就是在不改变 SofaRequestSofaResponse 核心结构的基础上,为其添加了新的功能。
  2. 不改变原有结构SofaRequestSofaResponse 对象的基本结构没有被改变,只是在原有的基础上添加了新的属性或信息。例如,decorateRequest 方法通过 request.addRequestProps(params) 为请求添加参数,并没有改变 SofaRequest 类的定义。
  3. 动态添加功能:可以根据不同的条件或配置,动态地决定是否执行这些额外的处理。例如,通过 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 兼容,体现了适配器模式的应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值