tars源码解析二

tars源码解析二

准备知识

这节准备解析下tars的网络层和协议转换的源码,为了更好的理解这节,需要具备java网络层知识,主要是nio和Reactor模式。

nio的知识可以参考这个系列的文章
http://www.iteye.com/magazines/132-Java-NIO

这里简单说下Nio的概念,在bio模式下,因为socket读取数据是阻塞的,所以导致整个线程都卡在这里。在nio模式下,会用事件机制(表相是这样,底层是依赖操作系统的机制实现,linux的epoll,windows的iocp)用一个线程去托管n个socket,该线程不停的遍历所有socket的事件,如果某个socket读取好了数据发送通知通知该线程,该线程拿出该socket去处理(通俗的解释,不严格正确)

Reactor模式
https://www.cnblogs.com/doit8791/p/7461479.html

Reactor有几张图很重要,代表了几种模式

  • 单个线程充当Reactor,业务也在该线程里处理
    tars源码解析二_第1张图片

  • 单个线程充当Reactor,业务放在线程池处理
    tars源码解析二_第2张图片

  • Reactor拆出来2个,1个负责连接,1个负责读,业务放在线程池处理
    tars源码解析二_第3张图片

这3张图理解了,基本上Ractor模式就掌握了

服务端的网络层

上一节我们忽略了整个网络层的细节,对于上层来说,就是这段的代码
ServantAdapter.bind

public void bind(AppService appService) throws IOException {
        this.skeleton = (ServantHomeSkeleton) appService;
        ServerConfig serverCfg = ConfigurationManager.getInstance().getServerConfig();

        boolean keepAlive = true;
        Codec codec = createCodec(serverCfg);
        Processor processor = createProcessor(serverCfg);
        Executor threadPool = ServantThreadPoolManager.get(servantAdapterConfig);

        Endpoint endpoint = this.servantAdapterConfig.getEndpoint();
        if (endpoint.type().equals("tcp")) {
            this.selectorManager = new SelectorManager(Utils.getSelectorPoolSize(), new ServantProtocolFactory(codec), threadPool, processor, keepAlive, "server-tcp-reactor", false);
            this.selectorManager.setTcpNoDelay(serverCfg.isTcpNoDelay());
            this.selectorManager.start();

            System.out.println("[SERVER] server starting at " + endpoint + "...");
            ServerSocketChannel serverChannel = ServerSocketChannel.open();
            serverChannel.socket().bind(new InetSocketAddress(endpoint.host(), endpoint.port()), 1024);
            serverChannel.configureBlocking(false);

            selectorManager.getReactor(0).registerChannel(serverChannel, SelectionKey.OP_ACCEPT);

            System.out.println("[SERVER] server started at " + endpoint + "...");

        } else if (endpoint.type().equals("udp")) {

            this.selectorManager = new SelectorManager(1, new ServantProtocolFactory(codec), threadPool, processor, false, "server-udp-reactor", true);
            this.selectorManager.start();

            System.out.println("[SERVER] server starting at " + endpoint + "...");
            DatagramChannel serverChannel = DatagramChannel.open();
            DatagramSocket socket = serverChannel.socket();
            socket.bind(new InetSocketAddress(endpoint.host(), endpoint.port()));
            serverChannel.configureBlocking(false);

            this.selectorManager.getReactor(0).registerChannel(serverChannel, SelectionKey.OP_READ);
            System.out.println("[SERVER] servant started at " + endpoint + "...");
        }
    }

先看 SelectorManager初始化
获取线程池的数目,我们大部分确定线程数一般是cpu+1,但是这里对于cpu大于8的,做了特殊处理,原因猜测是因为如果cpu大于8的时候,不想把cpu都占满?

public class Utils {
    public static int getSelectorPoolSize() {
        int processors = Runtime.getRuntime().availableProcessors();
        return processors > 8 ? 4 + (processors * 5 / 8) : processors + 1;
    }

我们看下SelectorManager成员变量

public final class SelectorManager {

    private final AtomicLong sets = new AtomicLong(0);

    //这里是前面说的reactor模式里的reactor
    private final Reactor[] reactorSet;

    //这里是codec的工厂,理解简单点就是codec,默认的情况是TarsCodec这个类
    private ProtocolFactory protocolFactory = null;

    //这个treadPool是处理业务的线程池
    private Executor threadPool = null;

    //request<-> response的处理逻辑 默认是TarsServantProcessor
    private Processor processor = null;

    //select的个数,基本就是reactor的个数
    private final int selectorPoolSize;

    private volatile boolean started;

    private boolean keepAlive;

    private boolean isTcpNoDelay = false;

selectManager的构造函数,主要是赋值,构建Reactors

public SelectorManager(int selectorPoolSize, ProtocolFactory protocolFactory, Executor threadPool,
                           Processor processor, boolean keepAlive, String reactorNamePrefix, boolean udpMode) throws IOException {
    if (udpMode) selectorPoolSize = 1;

    this.selectorPoolSize = selectorPoolSize;
    this.protocolFactory = protocolFactory;
    this.threadPool = threadPool;
    this.processor = processor;
    this.keepAlive = keepAlive;

    reactorSet = new Reactor[selectorPoolSize];

    for (int i = 0; i < reactorSet.length; i++) {
        reactorSet[i] = new Reactor(this, reactorNamePrefix + "-" + protocolFactory.getClass().getSimpleName().toLowerCase() + "-" + String.valueOf(i), udpMode);
    }
}

整个网络层,tars提供了一个单独的net包来封装

我们看下Reactor,可以看到Reactor是个Thread,里面包含nio的一个重要组件select

public final class Reactor extends Thread {

    protected volatile Selector selector = null;

    private volatile boolean crashed = false;

    private final Queue register = new LinkedBlockingQueue();

    private final Queue unregister = new LinkedBlockingQueue();

    private Acceptor acceptor = null;

    public Reactor(SelectorManager selectorManager, String name) throws IOException {
        this(selectorManager, name, false);
    }

    public Reactor(SelectorManager selectorManager, String name, boolean udpMode) throws IOException {
        super(name);

        if (udpMode) {
            this.acceptor = new UDPAcceptor(selectorManager);
        } else {
            this.acceptor = new TCPAcceptor(selectorManager);
        }

        this.selector = Selector.open();
    }

accpetor是处理网络事件的逻辑

public abstract class Acceptor {

    protected SelectorManager selectorManager = null;

    public Acceptor(SelectorManager selectorManager) {
        this.selectorManager = selectorManager;
    }

    //处理网络OP_CONNECT的事件
    public abstract void handleConnectEvent(SelectionKey key) throws IOException;

    //处理网络OP_ACCEPT的事件
    public abstract void handleAcceptEvent(SelectionKey key) throws IOException;

    //处理网络OP_READ的事件
    public abstract void handleReadEvent(SelectionKey key) throws IOException;

    //处理网络OP_WRITE的事件
    public abstract void handleWriteEvent(SelectionKey key) throws IOException;
}

我们稍后再看下TCPAcceptor具体的处理逻辑,回到上面
this.selectorManager.start();
这里启动了Reactor,其实就是调用Thread的run方法,这里基本属于nio的处理,可以简单理解,不停的循环遍历socket看看有没有事件抛出来,抛出来就处理。

public synchronized void start() {
    if (this.started) {
        return;
    }

    this.started = true;
    for (Reactor reactor : this.reactorSet) {
        reactor.start();
    }
}


public void run() {
    try {
        while (!Thread.interrupted()) {
            selector.select();
            processRegister();
            Iterator iter = selector.selectedKeys().iterator();
            while (iter.hasNext()) {
                SelectionKey key = iter.next();
                iter.remove();

                if (!key.isValid()) continue;

                try {
                    //1. Update the last operation time
                    if (key.attachment() != null && key.attachment() instanceof Session) {
                        ((Session) key.attachment()).updateLastOperationTime();
                    }

                    //2. Dispatch I/O event
                    dispatchEvent(key);

                } catch (Throwable ex) {
                    disConnectWithException(key, ex);
                }
            }
            processUnRegister();
        }
    } catch (Throwable e) {
        crashed = true;
        e.printStackTrace();
    } finally {
        try {
            selector.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

抛出来的事件怎么处理,在dispatchEvent处理,其实是调用前面的acceptor去处理

private void dispatchEvent(final SelectionKey key) throws IOException {
    if (key.isConnectable()) {
        acceptor.handleConnectEvent(key);
    } else if (key.isAcceptable()) {
        acceptor.handleAcceptEvent(key);
    } else if (key.isReadable()) {
        acceptor.handleReadEvent(key);
    } else if (key.isValid() && key.isWritable()) {
        acceptor.handleWriteEvent(key);
    }
}

先不急着看acceptor的逻辑,我们停下来想想,这里的reactor线程会发生什么?会走哪个逻辑?如果你熟悉网络编程的话,你会发现这个服务端还没绑定端口。回到上层

启动ractor之后,绑定端口,随后注册OP_ACCEPT消息

ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.socket().bind(new InetSocketAddress(endpoint.host(), endpoint.port()), 1024);
serverChannel.configureBlocking(false);

selectorManager.getReactor(0).registerChannel(serverChannel, SelectionKey.OP_ACCEPT);

System.out.println("[SERVER] server started at " + endpoint + "...");

我们现在来看TcpAcceptor处理逻辑, 先看怎么处理OP_ACCEPT,如果一个客户端连接过来,建立一个TCPSession,然后注册到SessionManager里管理,同时使用一个新的reactor来处理这个connection的OP_READ的消息。

public void handleAcceptEvent(SelectionKey key) throws IOException {
    //1. Accept TCP request
    ServerSocketChannel server = (ServerSocketChannel) key.channel();
    SocketChannel channel = server.accept();
    channel.socket().setTcpNoDelay(selectorManager.isTcpNoDelay());
    channel.configureBlocking(false);
    Utils.setQosFlag(channel.socket());

    //2. Create NioSession for each TCP connection
    TCPSession session = new TCPSession(selectorManager);
    session.setChannel(channel);
    session.setStatus(SessionStatus.SERVER_CONNECTED);
    session.setKeepAlive(selectorManager.isKeepAlive());
    session.setTcpNoDelay(selectorManager.isTcpNoDelay());

    //3. Register session
    SessionManager.getSessionManager().registerSession(session);

    //4. Register channel with the specified session
    selectorManager.nextReactor().registerChannel(channel, SelectionKey.OP_READ, session);
}

这里符合我们前面讲的reactor第三张图,一个reactor去处理连接请求,连接上来让一个reactor组来处理读消息分散压力,本应该如此,但是我们看tars的nextReactor这个方法.假设我们有2个reactor,我们固定用第0个reactor去处理连接,但是这么写会导致我第0个reactor也会处理read消息,其实不是很符合reactor的标准设计思路。不知道tars这么设计的理由?

public final Reactor nextReactor() {
    return this.reactorSet[(int) (this.sets.incrementAndGet() % this.selectorPoolSize)];
}

看下OP_READ消息处理,调用TCPSession的read方法

public void handleReadEvent(SelectionKey key) throws IOException {
    TCPSession session = (TCPSession) key.attachment();
    if (session == null) throw new RuntimeException("The session is null when reading data...");
    session.read();
}

TCPSession也很好理解,就是一个connection。这里的处理都是nio的部分,如果对nio很熟悉,很好理解,简单来说
1. 把读取的二进制数据调用我们前面说的codec去处理获取request
2. 把request形成一个workTread丢到业务线程池里取处理

protected void read() throws IOException {
    int ret = readChannel();

    if (this.status == SessionStatus.CLIENT_CONNECTED) {
        readResponse();
    } else if (this.status == SessionStatus.SERVER_CONNECTED) {
        readRequest();
    } else {
        throw new IllegalStateException("The current session status is invalid. [status:" + this.status + "]");
    }

    if (ret < 0) {
        close();
        return;
    }
}

public void readRequest() throws IOException {
    Request request = null;
    IoBuffer tempBuffer = null;

    try {
        tempBuffer = readBuffer.duplicate().flip();

        while (true) {
            tempBuffer.mark();

            if (tempBuffer.remaining() > 0) {
                request = selectorManager.getProtocolFactory().getDecoder().decodeRequest(tempBuffer, this);
            } else {
                request = null;
            }

            if (request != null) {
                try {
                    request.resetBornTime();
                    selectorManager.getThreadPool().execute(new WorkThread(request, selectorManager));
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            } else {
                tempBuffer.reset();
                readBuffer = resetIoBuffer(tempBuffer);
                break;
            }
        }
    } catch (ProtocolException ex) {
        close();
        ex.printStackTrace();
    }
}

我们先看codec的decodeRequest的逻辑,这里是在TarsCodec里,这里如果想看懂,需要对tars通信协议具备一定理解,我们可以不关心这个。
简单理解就是一个4字节的int去表明我这个消息长度是多少

public Request decodeRequest(IoBuffer buffer, Session session) throws ProtocolException {
    //如果一个int都没有返回
    if (buffer.remaining() < 4) {
        return null;
    }
    //算下真个包体多长,如果包体太长,认为出错,buffer.getInt()就是这个包体的长度
    int length = buffer.getInt() - TarsHelper.HEAD_SIZE;
    if (length > TarsHelper.PACKAGE_MAX_LENGTH || length <= 0) {
        throw new ProtocolException("the length header of the package must be between 0~10M bytes. data length:" + Integer.toHexString(length));
    }
    //如果接受的消息长度不足,说明还需要读取,返回继续读
    if (buffer.remaining() < length) {
        return null;
    }

    //这里就是把二进制数据按照协议的格式去填充进TarsServantRequest
    byte[] reads = new byte[length];
    buffer.get(reads);
    TarsInputStream jis = new TarsInputStream(reads);
    TarsServantRequest request = new TarsServantRequest(session);
    try {
        short version = jis.read(TarsHelper.STAMP_SHORT.shortValue(), 1, true);
        byte packetType = jis.read(TarsHelper.STAMP_BYTE.byteValue(), 2, true);
        int messageType = jis.read(TarsHelper.STAMP_INT.intValue(), 3, true);
        int requestId = jis.read(TarsHelper.STAMP_INT.intValue(), 4, true);
        String servantName = jis.readString(5, true);
        String methodName = jis.readString(6, true);
        request.setVersion(version);
        request.setPacketType(packetType);
        request.setMessageType(messageType);
        request.setRequestId(requestId);
        request.setServantName(servantName);
        request.setFunctionName(methodName);
        request.setInputStream(jis);
        request.setCharsetName(charsetName);
    } catch (Exception e) {
        System.err.println(e);
        request.setRet(TarsHelper.SERVERDECODEERR);
    }
    return request;
}

在我们开始看第二步之前,我们思考一个问题,我们不停的读取出来Request的,tars能不能保证同一个session执行的先后顺序。比如同一个客户端连续发送了消息abc,希望接受的顺序也是abc,tars能不能做到呢?

答案是no,tars做不到,为什么?因为消息abc是交由线程池处理,也许消息c先处理完就先返回了,所以同一个session是无法保证顺序的。

如果我们想保证一个session的消息严格按顺序返回,怎么做(hits:可以参看mina源码,mina是可以做到的)

做不到顺序,会对rpc有影响么?答案是no,具体答案在客户端实现里
从这里可以看到我们不能把Tars当做消息队列来使用,因为消息队列要严格顺序

我们再看第二步如何处理的session,主要是看WorkThread的执行方法
“`java
public void run() {
try {
if (udpSession != null) {
parseDatagramPacket();
udpBuffer = null;
}

    if (req != null) {
        req.setProcessTime(System.currentTimeMillis());
        req.init();
        Response res = selectorManager.getProcessor().process(req, req.getIoSession());
        if (!res.isAsyncMode()) req.getIoSession().write(res);
    } else if (resp != null) {
        resp.init();
        Ticket ticket = TicketManager.getTicket(resp.getTicketNumber());
        if (ticket == null) {
            String s = "failed to fetch request for this response. [from:" + resp.getSession().getRemoteIp() + ":" + resp.getSession().getRemotePort() + "]";
            System.out.println(s);
            return;
        }
        fillDitributedContext(ticket.request().getDistributedContext());
        ticket.notifyResponse(resp);
        ticket.countDown();
        TicketManager.removeTicket(ticket.getTicketNumber());
    }
} catch (Exception ex) {
    ex.printStackTrace();
} finally {
    clearDistributedContext();
}

}
“`
这里先调用req.init方法,这里会继续解析request消息。

这里为啥把request的解析分成2个部分,这里体现了设计的精巧,因为第一部分解析是在reactor线程里解析,所以需要尽量快,防止阻塞,而这里的body解析是在业务线程里解析。(深刻理解了Reactor模式)

public void init() {
    ((TarsCodec) this.session.getProtocolFactory().getDecoder()).decodeRequestBody(this);
//        TarsCodecHelper.decodeRequestBody(this);
}
 public ServantRequest decodeRequestBody(ServantRequest req) {
    TarsServantRequest request = (TarsServantRequest) req;
    if (request.getRet() != TarsHelper.SERVERSUCCESS) {
        return request;
    }
    if (TarsHelper.isPing(request.getFunctionName())) {
        return request;
    }

    TarsInputStream jis = request.getInputStream();
    ClassLoader oldClassLoader = null;
    try {
        oldClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(resolveProtocolClassLoader());
        String methodName = request.getFunctionName();
        byte[] data = jis.read(TarsHelper.STAMP_BYTE_ARRAY, 7, true);//数据
        int timeout = jis.read(TarsHelper.STAMP_INT.intValue(), 8, true);//超时时间
        Map context = (Map) jis.read(TarsHelper.STAMP_MAP, 9, true);//Map context
        Map status = (Map) jis.read(TarsHelper.STAMP_MAP, 10, true);

        request.setTimeout(timeout);
        request.setContext(context);
        request.setStatus(status);

        String servantName = request.getServantName();
        //在启动的时候会把暴露的接口的方法和参数缓存起来,这里就是取出来
        Map methodInfoMap = AnalystManager.getInstance().getMethodMapByName(servantName);

        if (methodInfoMap == null || methodInfoMap.isEmpty()) {
            request.setRet(TarsHelper.SERVERNOSERVANTERR);
            throw new ProtocolException("no found methodInfo, the context[ROOT], serviceName[" + servantName + "], methodName[" + methodName + "]");
        }
        TarsMethodInfo methodInfo = methodInfoMap.get(methodName);
        if (methodInfo == null) {
            request.setRet(TarsHelper.SERVERNOFUNCERR);
            throw new ProtocolException("no found methodInfo, the context[ROOT], serviceName[" + servantName + "], methodName[" + methodName + "]");
        }

        request.setMethodInfo(methodInfo);
        List parametersList = methodInfo.getParametersList();
        if (!CommonUtils.isEmptyCollection(parametersList)) {
            Object[] parameters = new Object[parametersList.size()];
            int i = 0;
            //不同版本解析不同
            if (TarsHelper.VERSION == request.getVersion()) {//request
                parameters = decodeRequestBody(data, request.getCharsetName(), methodInfo);
            } else if (TarsHelper.VERSION2 == request.getVersion() || TarsHelper.VERSION3 == request.getVersion()) {
                //wup request
                UniAttribute unaIn = new UniAttribute();
                unaIn.setEncodeName(request.getCharsetName());

                if (request.getVersion() == TarsHelper.VERSION2) {
                    unaIn.decodeVersion2(data);
                } else if (request.getVersion() == TarsHelper.VERSION3) {
                    unaIn.decodeVersion3(data);
                }

                Object value = null;
                for (TarsMethodParameterInfo parameterInfo : parametersList) {
                    if (TarsHelper.isHolder(parameterInfo.getAnnotations())) {
                        String holderName = TarsHelper.getHolderName(parameterInfo.getAnnotations());
                        if (!StringUtils.isEmpty(holderName)) {
                            value = new Holder(unaIn.getByClass(holderName, parameterInfo.getStamp()));
                        } else {
                            value = new Holder();
                        }
                    } else {
                        value = unaIn.getByClass(parameterInfo.getName(), parameterInfo.getStamp());
                    }
                    parameters[i++] = value;
                }
            } else {
                request.setRet(TarsHelper.SERVERDECODEERR);
                System.err.println("un supported protocol, ver=" + request.getVersion());
            }
            request.setMethodParameters(parameters);
        }
    } catch (Throwable ex) {
        if (request.getRet() == TarsHelper.SERVERSUCCESS) {
            request.setRet(TarsHelper.SERVERDECODEERR);
        }
        System.err.println(TarsUtil.getHexdump(jis.getBs()));
    } finally {
        if (oldClassLoader != null) {
            Thread.currentThread().setContextClassLoader(oldClassLoader);
        }
    }
    return request;
} 
  

这里分三个版本
Verson1:
TarsInputStream的read方法,前面是如果参数是基本类型,用read解析,如果是复杂的对象类型调用TarsInputStreamExt.read处理

“`java
protected Object[] decodeRequestBody(byte[] data, String charset, TarsMethodInfo methodInfo) throws Exception {
TarsInputStream jis = new TarsInputStream(data);
List parametersList = methodInfo.getParametersList();
Object[] parameters = new Object[parametersList.size()];
int i = 0;

    jis.setServerEncoding(charset);//set decode charset name
    Object value = null;
    for (TarsMethodParameterInfo parameterInfo : parametersList) {
        if (TarsHelper.isHolder(parameterInfo.getAnnotations())) {
            value = new Holder(jis.read(parameterInfo.getStamp(), parameterInfo.getOrder(), false));
        } else {
            value = jis.read(parameterInfo.getStamp(), parameterInfo.getOrder(), false);
        }
        parameters[i++] = value;
    }

    return parameters;
}
 
  


```java
public  Object read(T o, int tag, boolean isRequire) {
        if (o instanceof Byte) {
            return Byte.valueOf(read((byte) 0x0, tag, isRequire));
        } else if (o instanceof Boolean) {
            return Boolean.valueOf(read(false, tag, isRequire));
        } else if (o instanceof Short) {
            return Short.valueOf(read((short) 0, tag, isRequire));
        } else if (o instanceof Integer) {
            int i = read((int) 0, tag, isRequire);
            return Integer.valueOf(i);
        } else if (o instanceof Long) {
            return Long.valueOf(read((long) 0, tag, isRequire));
        } else if (o instanceof Float) {
            return Float.valueOf(read((float) 0, tag, isRequire));
        } else if (o instanceof Double) {
            return Double.valueOf(read((double) 0, tag, isRequire));
        } else if (o instanceof String) {
            return readString(tag, isRequire);
        } else if (o instanceof Map) {
            return readMap((Map) o, tag, isRequire);
        } else if (o instanceof List) {
            return readArray((List) o, tag, isRequire);
        } else if (o instanceof TarsStructBase) {
            return read((TarsStructBase) o, tag, isRequire);
        } else if (o.getClass().isArray()) {
            if (o instanceof byte[] || o instanceof Byte[]) {
                return read((byte[]) null, tag, isRequire);
            } else if (o instanceof boolean[]) {
                return read((boolean[]) null, tag, isRequire);
            } else if (o instanceof short[]) {
                return read((short[]) null, tag, isRequire);
            } else if (o instanceof int[]) {
                return read((int[]) null, tag, isRequire);
            } else if (o instanceof long[]) {
                return read((long[]) null, tag, isRequire);
            } else if (o instanceof float[]) {
                return read((float[]) null, tag, isRequire);
            } else if (o instanceof double[]) {
                return read((double[]) null, tag, isRequire);
            } else {
                return readArray((Object[]) o, tag, isRequire);
            }
        } else {
            return TarsInputStreamExt.read(o, tag, isRequire, this);
        }
    }




class="se-preview-section-delimiter">div>
public class TarsInputStreamExt {

    public static  T read(T e, int tag, boolean isRequire, TarsInputStream jis) {
        TarsStructInfo info = TarsHelper.getStructInfo(e.getClass());

        if (info == null) {
            throw new TarsDecodeException("the JavaBean[" + e.getClass().getSimpleName() + "] no annotation Struct");
        }

        if (jis.skipToTag(tag)) {
            HeadData hd = new HeadData();
            jis.readHead(hd);
            if (hd.type != TarsStructBase.STRUCT_BEGIN) {
                throw new TarsDecodeException("type mismatch.");
            }

            T result = (T) CommonUtils.newInstance(e.getClass());

            List list = info.getPropertyList();
            if (!CommonUtils.isEmptyCollection(list)) {
                for (TarsStrutPropertyInfo propertyInfo : list) {
                    Object value = jis.read(propertyInfo.getStamp(), propertyInfo.getOrder(), propertyInfo.isRequire());
                    BeanAccessor.setBeanValue(result, propertyInfo.getName(), value);
                }
            }
            jis.skipToStructEnd();
            return result;

        } else if (isRequire) {
            throw new TarsDecodeException("require field not exist.");
        }
        return null;
    }
}




"se-preview-section-delimiter">

其中version2,3都是利用UniAttribute unaIn = new UniAttribute();

Version2:
数据data—>HashMap


```java
public class TarsInputStreamExt {

    public static  T read(T e, int tag, boolean isRequire, TarsInputStream jis) {
        TarsStructInfo info = TarsHelper.getStructInfo(e.getClass());

        if (info == null) {
            throw new TarsDecodeException("the JavaBean[" + e.getClass().getSimpleName() + "] no annotation Struct");
        }

        if (jis.skipToTag(tag)) {
            HeadData hd = new HeadData();
            jis.readHead(hd);
            if (hd.type != TarsStructBase.STRUCT_BEGIN) {
                throw new TarsDecodeException("type mismatch.");
            }

            T result = (T) CommonUtils.newInstance(e.getClass());

            List list = info.getPropertyList();
            if (!CommonUtils.isEmptyCollection(list)) {
                for (TarsStrutPropertyInfo propertyInfo : list) {
                    Object value = jis.read(propertyInfo.getStamp(), propertyInfo.getOrder(), propertyInfo.isRequire());
                    BeanAccessor.setBeanValue(result, propertyInfo.getName(), value);
                }
            }
            jis.skipToStructEnd();
            return result;

        } else if (isRequire) {
            throw new TarsDecodeException("require field not exist.");
        }
        return null;
    }
}




"se-preview-section-delimiter">

其中version2,3都是利用UniAttribute unaIn = new UniAttribute();
Version2:

public void decode(byte[] buffer) {
    _is.warp(buffer);
    _is.setServerEncoding(encodeName);
    HashMapbyte[]>> _tempdata = new HashMapbyte[]>>(1);
    HashMapbyte[]> h = new HashMapbyte[]>(1);
    h.put("", new byte[0]);
    _tempdata.put("", h);
    _data = (HashMapbyte[]>>) _is.readMap(_tempdata, 0, false);
}
其中version2,3都是利用UniAttribute unaIn = new UniAttribute();
Version2:
```java
public void decode(byte[] buffer) {
    _is.warp(buffer);
    _is.setServerEncoding(encodeName);
    HashMapbyte[]>> _tempdata = new HashMapbyte[]>>(1);
    HashMapbyte[]> h = new HashMapbyte[]>(1);
    h.put("", new byte[0]);
    _tempdata.put("", h);
    _data = (HashMapbyte[]>>) _is.readMap(_tempdata, 0, false);
}




"se-preview-section-delimiter">

Version3:

public void decodeVersion3(byte[] buffer) {
    _is.warp(buffer);
    _is.setServerEncoding(encodeName);
    HashMapbyte[]> _tempdata = new HashMapbyte[]>(1);
    _tempdata.put("", new byte[0]);
    _newData = (HashMapbyte[]>) _is.readMap(_tempdata, 0, false);
}
Version3:
```java
public void decodeVersion3(byte[] buffer) {
    _is.warp(buffer);
    _is.setServerEncoding(encodeName);
    HashMapbyte[]> _tempdata = new HashMapbyte[]>(1);
    _tempdata.put("", new byte[0]);
    _newData = (HashMapbyte[]>) _is.readMap(_tempdata, 0, false);
}




"se-preview-section-delimiter">

最后解析的过程:

public  T getByClass(String name, T proxy) throws ObjectCreateException {
    if (null != _newData) {
        if (!_newData.containsKey(name)) {
            return null;
        } else if (cachedData.containsKey(name)) {
            return (T) cachedData.get(name);
        } else {
            byte[] data = _newData.get(name);
            try {
                Object o = decodeData(data, proxy);
                if (null != o) {
                    saveDataCache(name, o);
                }
                return (T) o;
            } catch (Exception ex) {
                throw new ObjectCreateException(ex);
            }
        }
    } else {
        if (!_data.containsKey(name)) {
            return null;
        } else if (cachedData.containsKey(name)) {
            return (T) cachedData.get(name);
        } else {
            HashMapbyte[]> pair = _data.get(name);
            String className = null;
            byte[] data = new byte[0];
            for (Entrybyte[]> e : pair.entrySet()) {
                className = e.getKey();
                data = e.getValue();
                break;
            }
            try {
                _is.warp(data);
                _is.setServerEncoding(encodeName);
                Object o = _is.read(proxy, 0, true);
                saveDataCache(name, o);
                return (T) o;
            } catch (Exception ex) {
                throw new ObjectCreateException(ex);
            }
        }
    }

}




"se-preview-section-delimiter">

其中saveDataCache是设立缓存,防止一次调用传递多次参数?(不是很懂???)
这样就把参数解析完毕
req.init();结束 这样所有的request全部解析完成

随后先调用process去处理request–>response转换,这里的process默认的是TarsServantProcessor

这里的核心就是找到ServantHomeSkeleton,然后调用真实的Method,执行invoke,获取返回值

在设计角度上看,在很多关键执行上,设计者提供了pre,post方法,比如在执行ServantHomeSkeleton.invoke 前会有preInvokeCapHomeSkeleton(),后会有postInvokeCapHomeSkeleton();

public Response process(Request req, Session session) {
    AppContainer container = null;
    TarsServantRequest request = null;
    TarsServantResponse response = null;
    ServantHomeSkeleton skeleton = null;
    Object value = null;
    AppContext appContext = null;
    ClassLoader oldClassLoader = null;
    int waitingTime = -1;
    long startTime = req.getProcessTime();
    String remark = "";

    try {
        oldClassLoader = Thread.currentThread().getContextClassLoader();
        request = (TarsServantRequest) req;
        //构建基础的response信息
        response = createResponse(request, session);
        response.setTicketNumber(req.getTicketNumber());

        if (response.getRet() != TarsHelper.SERVERSUCCESS || TarsHelper.isPing(request.getFunctionName())) {
            return response;
        }
        //判断开始处理消息的时间和接收消息的时间是否超时
        int maxWaitingTimeInQueue = ConfigurationManager.getInstance().getServerConfig().getServantAdapterConfMap().get(request.getServantName()).getQueueTimeout();
        waitingTime = (int) (startTime - req.getBornTime());
        if (waitingTime > maxWaitingTimeInQueue) {
            throw new TarsException("Wait too long, server busy.");
        }

        container = ContainerManager.getContainer(AppContainer.class);
        Context context = ContextManager.registerContext(request, response);
        context.setAttribute(Context.INTERNAL_START_TIME, startTime);
        context.setAttribute(Context.INTERNAL_CLIENT_IP, session.getRemoteIp());
        context.setAttribute(Context.INTERNAL_APP_NAME, container.getDefaultAppContext().name());
        context.setAttribute(Context.INTERNAL_SERVICE_NAME, request.getServantName());
        context.setAttribute(Context.INTERNAL_METHOD_NAME, request.getFunctionName());
        context.setAttribute(Context.INTERNAL_SESSION_DATA, session);

        //将request,response放到disDistributedContext里存储
        DistributedContext distributedContext = DistributedContextManager.getDistributedContext();
        distributedContext.put(DyeingSwitch.REQ, request);
        distributedContext.put(DyeingSwitch.RES, response);

        appContext = container.getDefaultAppContext();
        if (appContext == null) throw new RuntimeException("failed to find the application named:[ROOT]");

//            Thread.currentThread().setContextClassLoader(appContext.getAppContextClassLoader());
        //
        preInvokeSkeleton();
        skeleton = appContext.getCapHomeSkeleton(request.getServantName());
        if (skeleton == null) throw new RuntimeException("failed to find the servant named[" + request.getServantName() + "]");

        value = skeleton.invoke(request.getMethodInfo().getMethod(), request.getMethodParameters());
        response.setResult(value);
    } catch (Throwable cause) {
        System.out.println("ERROR: " + cause.getMessage());

        if (response.isAsyncMode()) {
            try {
                Context context = ContextManager.getContext();
                AsyncContext aContext = context.getAttribute(AsyncContext.PORTAL_CAP_ASYNC_CONTEXT_ATTRIBUTE);
                if (aContext != null) aContext.writeException(cause);
            } catch (Exception ex) {
                System.out.println("ERROR: " + ex.getMessage());
            }
        } else {
            response.setResult(null);
            response.setCause(cause);
            response.setRet(TarsHelper.SERVERUNKNOWNERR);
            remark = cause.toString();
        }
    } finally {
        if (oldClassLoader != null) {
            Thread.currentThread().setContextClassLoader(oldClassLoader);
        }
        ContextManager.releaseContext();
        if (!response.isAsyncMode()) {
            printServiceFlowLog(flowLogger, request, response.getRet(), (System.currentTimeMillis() - startTime), remark);
        }
        postInvokeSkeleton();
        OmServiceMngr.getInstance().reportWaitingTimeProperty(waitingTime);
        reportServerStat(request, response, startTime);
    }
    return response;
}




"se-preview-section-delimiter">

回到上层,如果不是异步模式,直接发送走,这里压根没看到异步模式的处理代码

这里写的看起来服务器是有异步,其实是没有的,异步是在客户端做的,服务器虽然有这个属性,但是没用(不知道为什么这么写)

if (!res.isAsyncMode()) req.getIoSession().write(res);




"se-preview-section-delimiter">
public void write(Response response) throws IOException {
    try {
        IoBuffer buffer = selectorManager.getProtocolFactory().getEncoder().encodeResponse(response, this);
        write(buffer);
    } catch (ProtocolException ex) {
        throw new IOException("protocol error:", ex);
    }
}





"se-preview-section-delimiter">

这里又回到了codec,默认是TarsCodec方法

public IoBuffer encodeResponse(Response resp, Session session) throws ProtocolException {
    TarsServantResponse response = (TarsServantResponse) resp;
    if (response.getPacketType() == TarsHelper.ONEWAY) {
        return null;
    }

    TarsOutputStream jos = new TarsOutputStream();
    jos.setServerEncoding(charsetName);
    try {
        jos.getByteBuffer().putInt(0);
        jos.write(response.getVersion(), 1);
        jos.write(response.getPacketType(), 2);

        if (response.getVersion() == TarsHelper.VERSION) {
            jos.write(response.getRequestId(), 3);
            jos.write(response.getMessageType(), 4);
            jos.write(response.getRet(), 5);
            jos.write(encodeResult(response, charsetName), 6);
            if (response.getStatus() != null) {
                jos.write(response.getStatus(), 7);
            }
            if (response.getRet() != TarsHelper.SERVERSUCCESS) {
                jos.write(StringUtils.isEmpty(response.getRemark()) ? "" : response.getRemark(), 8);
            }
        } else if (TarsHelper.VERSION2 == response.getVersion() || TarsHelper.VERSION3 == response.getVersion()) {
            jos.write(response.getMessageType(), 3);
            jos.write(response.getTicketNumber(), 4);
            String servantName = response.getRequest().getServantName();
            jos.write(servantName, 5);
            jos.write(response.getRequest().getFunctionName(), 6);
            jos.write(encodeWupResult(response, charsetName), 7);
            jos.write(response.getTimeout(), 8);
            if (response.getContext() != null) {
                jos.write(response.getContext(), 9);
            }
            if (response.getStatus() != null) {
                jos.write(response.getStatus(), 10);
            }
        } else {
            response.setRet(TarsHelper.SERVERENCODEERR);
            System.err.println("un supported protocol, ver=" + response.getVersion());
        }
    } catch (Exception ex) {
        if (response.getRet() == TarsHelper.SERVERSUCCESS) {
            response.setRet(TarsHelper.SERVERENCODEERR);
        }
    }
    ByteBuffer buffer = jos.getByteBuffer();
    int datalen = buffer.position();
    buffer.position(0);
    buffer.putInt(datalen);
    buffer.position(datalen);
    return IoBuffer.wrap(jos.toByteArray());
}




"se-preview-section-delimiter">

这里需要注意写的时候,是先写入一个queue,这个queue是个线程安全的queue,回到线程模型,这个TCPSession其实是被多个业务线程处理,所以写入的时候可能是多个线程同时写入。写完queque,然后注册消息到select上

private Queue queue = new LinkedBlockingQueue(1024 * 8);

protected void write(IoBuffer buffer) throws IOException {
    if (buffer == null) return;

    if (channel == null || key == null) throw new IOException("Connection is closed");

    if (!this.queue.offer(buffer.buf())) {
        throw new IOException("The session queue is full. [ queue size:" + queue.size() + " ]");
    }

    if (key != null) {
        key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
        key.selector().wakeup();
    }
}




"se-preview-section-delimiter">

回到TcpAcceptor

public void handleWriteEvent(SelectionKey key) throws IOException {
    TCPSession session = (TCPSession) key.attachment();
    if (session == null) throw new RuntimeException("The session is null when writing data...");
    session.doWrite();
}




"se-preview-section-delimiter">

实际调用TcpSession的doWriter方法,这里是用nio去写入

protected synchronized int doWrite() throws IOException {
    int writeBytes = 0;

    while (true) {
        ByteBuffer wBuf = queue.peek();

        if (wBuf == null) {
            key.interestOps(SelectionKey.OP_READ);

            if (queue.peek() != null) {
                key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
            }

            key.selector().wakeup();
            break;
        }

        int bytesWritten = ((SocketChannel) channel).write(wBuf);

        if (bytesWritten == 0 && wBuf.remaining() > 0) // Socket buffer is full.
        {
            key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
            key.selector().wakeup();
            break;
        }

        if (wBuf.remaining() == 0) {
            writeBytes++;
            queue.remove();
            continue;
        } else {
            return -1;
        }
    }

    if (!isKeepAlive()) close();

    return writeBytes;
}

这样整个发送过程就结束了。

整个服务端解析就基本结束了。

你可能感兴趣的:(Tars)