motan源码分析十一:部分特性

本章将描述motan部分的特性并对源码进行分析。

1.requestid的维护,使用了当前时间左移20位,再和一个自增变量组合

复制代码
public class RequestIdGenerator {
    protected static final AtomicLong offset = new AtomicLong(0);
    protected static final int BITS = 20;
    protected static final long MAX_COUNT_PER_MILLIS = 1 << BITS;


    /**
     * 获取 requestId
     * 
     * @return
     */
    public static long getRequestId() {
        long currentTime = System.currentTimeMillis();
        long count = offset.incrementAndGet();
        while(count >= MAX_COUNT_PER_MILLIS){
            synchronized (RequestIdGenerator.class){
                if(offset.get() >= MAX_COUNT_PER_MILLIS){
                    offset.set(0);
                }
            }
            count = offset.incrementAndGet();
        }
        return (currentTime << BITS) + count;
    }

    public static long getRequestIdFromClient() {
        // TODO 上下文 requestid
        return 0;

    }

}
复制代码

2.限流,motan支持简单的限流,是利用filter来实现的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@SpiMeta(name = "active")
@Activation(sequence = 1)
public class ActiveLimitFilter implements Filter {
 
    @Override
    public Response filter(Caller<?> caller, Request request) {
        int maxAcvitivyCount = caller.getUrl().getIntParameter(URLParamType.actives.getName(), URLParamType.actives.getIntValue());
        if (maxAcvitivyCount > 0) {
            int activeCount = RpcStats.getServiceStat(caller.getUrl()).getActiveCount();
            if (activeCount >= maxAcvitivyCount) {
                throw new MotanServiceException(String.format("Request(%s) active count exceed the limit (%s), referer:%s", request,
                        maxAcvitivyCount, caller.getUrl()), MotanErrorMsgConstant.SERVICE_REJECT);
            }
        }
 
        long startTime = System.currentTimeMillis();
        RpcStats.beforeCall(caller.getUrl(), request);
        try {
            Response rs = caller.call(request);
            RpcStats.afterCall(caller.getUrl(), request, true, System.currentTimeMillis() - startTime);
            return rs;
        } catch (RuntimeException re) {
            RpcStats.afterCall(caller.getUrl(), request, false, System.currentTimeMillis() - startTime);
            throw re;
        }
 
    }
 
}

3.对于连续失败的client进行不可用操作

复制代码
    void incrErrorCount() {
        long count = errorCount.incrementAndGet();

        // 如果节点是可用状态,同时当前连续失败的次数超过限制maxClientConnection次,那么把该节点标示为不可用
        if (count >= maxClientConnection && state.isAliveState()) {
            synchronized (this) {
                count = errorCount.longValue();

                if (count >= maxClientConnection && state.isAliveState()) {
                    LoggerUtil.error("NettyClient unavailable Error: url=" + url.getIdentity() + " "
                            + url.getServerPortStr());
                    state = ChannelState.UNALIVE;
                }
            }
        }
    }

    void resetErrorCount() {
        errorCount.set(0);

        if (state.isAliveState()) {
            return;
        }

        synchronized (this) {
            if (state.isAliveState()) {
                return;
            }

            // 如果节点是unalive才进行设置,而如果是 close 或者 uninit,那么直接忽略
            if (state.isUnAliveState()) {
                long count = errorCount.longValue();

                // 过程中有其他并发更新errorCount的,因此这里需要进行一次判断
                if (count < maxClientConnection) {
                    state = ChannelState.ALIVE;
                    LoggerUtil.info("NettyClient recover available: url=" + url.getIdentity() + " "
                            + url.getServerPortStr());
                }
            }
        }
    }
复制代码

4.支持多注册中心,因此cluster的refer集合是所有注册中心包含服务器的集合,如果同一个服务器在不同的注册中心注册,则cluster中当作两个服务器

5.服务端的采用boss线程池+工作线程池+业务线程池的处理方式

1
2
3
4
5
6
7
8
9
10
11
12
13
private final static ChannelFactory channelFactory = new NioServerSocketChannelFactory(//boss线程池和工作线程池,主要负责接收消息
        Executors.newCachedThreadPool(new DefaultThreadFactory("nettyServerBoss", true)),
        Executors.newCachedThreadPool(new DefaultThreadFactory("nettyServerWorker", true)));
 
    private StandardThreadExecutor standardThreadExecutor = null;//业务线程池,负责具体的业务处理
 
    standardThreadExecutor = (standardThreadExecutor != null && !standardThreadExecutor.isShutdown()) ? standardThreadExecutor
            : new StandardThreadExecutor(minWorkerThread, maxWorkerThread, workerQueueSize,
                    new DefaultThreadFactory("NettyServer-" + url.getServerPortStr(), true));
    standardThreadExecutor.prestartAllCoreThreads();
 
   final NettyChannelHandler handler = new NettyChannelHandler(NettyServer.this, messageHandler,
            standardThreadExecutor);//handler使用业务线程池今天处理具体的业务

  

posted @   【刘光亮】  阅读(1020)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
点击右上角即可分享
微信分享提示