Swift Server端源碼分析

Swift的網絡框架采用了jboss的netty庫,沒有采用后期的netty標準庫,它實現了純異步的Thrift服務器和客戶端框架,本篇主要介紹服務端的源碼。

1、以一個簡單的例子開始

我們以一個簡單的服務器端例子開始:

public static void main(String[] args) {
        //SwiftScribe scribeService = new SwiftScribe();

        ExecutorService taskWorkerExecutor;
        ThriftServer server;
        CountDownLatch latch;
        ExecutorService bossExecutor;
        ExecutorService ioWorkerExecutor;


        ThriftServiceProcessor processor = new ThriftServiceProcessor(
                new ThriftCodecManager(),
                ImmutableList.<ThriftEventHandler>of(),
                new SwiftScribe()
        );

        taskWorkerExecutor = newFixedThreadPool(1);

        ThriftServerDef serverDef = ThriftServerDef.newBuilder()
                                                   .listen(8899)
                                                   .withProcessor(processor)
                                                   .using(taskWorkerExecutor)
                                                   .build();

        bossExecutor = newCachedThreadPool();
        ioWorkerExecutor = newCachedThreadPool();

        NettyServerConfig serverConfig = NettyServerConfig.newBuilder()
                                                          .setBossThreadExecutor(bossExecutor)
                                                          .setWorkerThreadExecutor(ioWorkerExecutor)
                                                          .build();


        server = new ThriftServer(serverConfig, serverDef) ;
        server.start();
    }

我們首先封裝了ThriftServerDef和NettyServerConfig,由這兩個參數來構造一個ThriftServer對象,然后啟動Thrift服務。對外就可以提供端口號為8899的高并發的Thrift協議服務能力。

該服務對外提供的服務協議在SwiftScribe中,SwfitScribe的框架類是通過scribe.thrift文件生成,

namespace java com.facebook.swift.service.scribe

enum ResultCode
{
  OK,
  TRY_LATER
}

struct LogEntry
{
  1:  string category,
  2:  string message
}

service scribe
{
  ResultCode Log(1: list<LogEntry> messages);
}

生成框架的命令

java -jar .\swift-generator-cli-0.19.2-standalone.jar scribe.thrift -out ..\java

2、類說明

  • ThriftCodecManager類

ThriftCodecManager包含了所有已知的ThriftCodec的索引,并且可以按需為未知類型創建編解碼器。既然編解碼器的創建耗費資源, 所以每個類只有一個實例會被創建,并被ThriftCodecManager類管理起來。

目前包括的類型有:

public enum ThriftProtocolType
{
    UNKNOWN((byte) 0),
    BOOL((byte) 2),
    BYTE((byte) 3),
    DOUBLE((byte) 4),
    I16((byte) 6),
    I32((byte) 8),
    I64((byte) 10),
    STRING((byte) 11),
    STRUCT((byte) 12),
    MAP((byte) 13),
    SET((byte) 14),
    LIST((byte) 15),
    ENUM((byte) 8), // same as I32 type
    BINARY((byte) 11); // same as STRING type

    private final byte type;
}

在ThriftCodecManager中,通過typeCodecs管理所有數據類型的協議編解碼功能。

ThriftCodecManager通過Guawa庫中的LoadingCache來緩存每種Thrift類型的編解碼器,LoadingCache的原理這里大概說一下,如果能從緩存中找到到數據,那么就直接返回,否則會調用構造器中傳入的Callable對象的call方法,來根據key值計算出value,并緩存起來。然后才返回數據。

        addBuiltinCodec(new BooleanThriftCodec());
        addBuiltinCodec(new ByteThriftCodec());
        addBuiltinCodec(new ShortThriftCodec());
        addBuiltinCodec(new IntegerThriftCodec());
        addBuiltinCodec(new LongThriftCodec());
        addBuiltinCodec(new DoubleThriftCodec());
        addBuiltinCodec(new ByteBufferThriftCodec());
        addBuiltinCodec(new StringThriftCodec());
        addBuiltinCodec(new VoidThriftCodec());
        addBuiltinCodec(new BooleanArrayThriftCodec());
        addBuiltinCodec(new ShortArrayThriftCodec());
        addBuiltinCodec(new IntArrayThriftCodec());
        addBuiltinCodec(new LongArrayThriftCodec());
        addBuiltinCodec(new DoubleArrayThriftCodec());

以上是內在的類型以及相應的編解碼器,所以內在的類型是不需要計算,對于自定義的復雜的類型,就需要計算了,如:enum, set, map, struct等,由于初始化的時候,這些類型都是沒有放入編解碼器緩存的,所以第一次查詢肯定查詢不到。
這時候就需要計算:

 typeCodecs = CacheBuilder.newBuilder().build(new CacheLoader<ThriftType, ThriftCodec<?>>()
        {
            public ThriftCodec<?> load(ThriftType type)
                    throws Exception
            {
                try {
                    // When we need to load a codec for a type the first time, we push it on the
                    // thread-local stack before starting the load, and pop it off afterwards,
                    // so that we can detect recursive loads.
                    stack.get().push(type);

                    switch (type.getProtocolType()) {
                        case STRUCT: {
                            return factory.generateThriftTypeCodec(ThriftCodecManager.this, type.getStructMetadata());
                        }
                        case MAP: {
                            return new MapThriftCodec<>(type, getElementCodec(type.getKeyTypeReference()), getElementCodec(type.getValueTypeReference()));
                        }
                        case SET: {
                            return new SetThriftCodec<>(type, getElementCodec(type.getValueTypeReference()));
                        }
                        case LIST: {
                            return new ListThriftCodec<>(type, getElementCodec(type.getValueTypeReference()));
                        }
                        case ENUM: {
                            return new EnumThriftCodec<>(type);
                        }
                        default:
                            if (type.isCoerced()) {
                                ThriftCodec<?> codec = getCodec(type.getUncoercedType());
                                TypeCoercion coercion = catalog.getDefaultCoercion(type.getJavaType());
                                return new CoercionThriftCodec<>(codec, coercion);
                            }
                            throw new IllegalArgumentException("Unsupported Thrift type " + type);
                    }
                }
                finally {
                    ThriftType top = stack.get().pop();
                    checkState(type.equals(top),
                               "ThriftCatalog circularity detection stack is corrupt: expected %s, but got %s",
                               type,
                               top);
                }

            }
        });

從ThriftCodecManager中獲取編解碼器主要有兩種方法:getCodec和getCodecIfpresent,前者不存在會調用前面的builder方法構建,后者不存在返回null。

ThriftCodecManager包含一個 ThriftCatalog対像,ThriftCatalog包含了所有已知的structs, enums,和類型隱含轉換器。因為元數據抽取是非常消耗資源的,所有catalog是單實例。

ThriftCatalog的構造函數如下,它采用DefaultJavaCorecions類來加載隱式類型轉換器。

    @VisibleForTesting
    public ThriftCatalog(Monitor monitor)
    {
        this.monitor = monitor;
        addDefaultCoercions(DefaultJavaCoercions.class);
    }

可以用來轉換一些原始數據類型:int,long,double,float,byte,byte[]
在DefaultJavaCoercions類中,包含了一些方法,用來轉換原始類型和對應的封裝類型,這些方法都有注解,注解有兩種類型:FromThrift和ToThrift。
在addDefaultCoercions中,根據不同的注解,會把相應的方法,放到
Map<ThriftType, Method> toThriftCoercions = new HashMap<>();
Map<ThriftType, Method> fromThriftCoercions = new HashMap<>();
兩個結構體中。最后把每種類型的的FromThrift方法和ToThrift方法封裝成一個類型轉換器,存放到隱式轉換映射中。

        Map<Type, TypeCoercion> coercions = new HashMap<>();
        for (Map.Entry<ThriftType, Method> entry : toThriftCoercions.entrySet()) {
            ThriftType type = entry.getKey();
            Method toThriftMethod = entry.getValue();
            Method fromThriftMethod = fromThriftCoercions.get(type);
            TypeCoercion coercion = new TypeCoercion(type, toThriftMethod, fromThriftMethod);
            coercions.put(type.getJavaType(), coercion);
        }
        this.coercions.putAll(coercions);
  • ThriftServiceProcessor類

用來封裝一個Thrift服務。

    private final Map<String, ThriftMethodProcessor> methods;
    private final List<ThriftEventHandler> eventHandlers;

methods存儲每個方法的處理器對象,而eventHandlers則是用于給外部監控用,類的定義如下所示:

public abstract class ThriftEventHandler
{
    public Object getContext(String methodName, RequestContext requestContext)
    {
        return null;
    }

    public void preRead(Object context, String methodName) {}
    public void postRead(Object context, String methodName, Object[] args) {}
    public void preWrite(Object context, String methodName, Object result) {}
    public void preWriteException(Object context, String methodName, Throwable t) {}
    public void postWrite(Object context, String methodName, Object result) {}
    public void postWriteException(Object context, String methodName, Throwable t) {}
    public void declaredUserException(Object o, String methodName, Throwable t, ThriftCodec<?> exceptionCodec) {}
    public void undeclaredUserException(Object o, String methodName, Throwable t) {}
    public void done(Object context, String methodName) {}
}

這里說明一下process函數
1)讀取Thrift的消息開始信息
2)獲取Thrift方法名和SequenceId(這兩個信息都在開始信息里面,還包括信息類型是CALL還是ONEWAY)
3)根據Thrift方法名,獲取對應的處理器:ThriftMethodProcessor
4)生成ContextChain對象
5)調用方法處理器進行處理。
6)添加回調。這里的回調是基于google的Futures來做的。
但是這個回調感覺對于那些需要直接返回的函數,會增加額外的開銷,因為回調的處理,需要放到另外一個執行器中處理(線程池),然后才會返回。

  • ThriftServiceMetadata類

成員說明:

    private final String name;  // 服務名,通過@ThriftService("名字")注解讀取出來的名字。
    private final Map<String, ThriftMethodMetadata> methods;  // 類所有的Thrift方法,包括父類和所有接口的

Thrift方法

    private final Map<String, ThriftMethodMetadata> declaredMethods; // 類本身自己定義的Thrift方法
    private final ImmutableList<ThriftServiceMetadata> parentServices;  // 父類或者接口定義的元數據
    private final ImmutableList<String> documentation;   // 通過ThriftDocumentation注解的文檔
  • ThriftMethodMetadata類

成員說明:

    private final String name; // 方法名
    private final String qualifiedName; // 服務名.方法名
    private final ThriftType returnType;
    private final List<ThriftFieldMetadata> parameters;
    private final Method method;
    private final ImmutableMap<Short, ThriftType> exceptions;
    private final ImmutableList<String> documentation;
    private final boolean oneway;

類的構造過程:(以例子中的Scribe.log為例進行說明)
1)獲取參數編號,一般為index+1
2)獲取參數名稱
3)根據返回值和參數的Java類型獲取(或者按需生成)對應的ThriftType【ThriftType thriftType = catalog.getThriftType(parameterType);】
獲取的邏輯:如果緩存中有,則從緩存中獲取,否則調用 buildThriftType-->buildThriftTypeInternal來生成。
buildThriftTypeInternal的邏輯大致為:
i)獲取類型的裸類型Class<?> rawType = TypeToken.of(javaType).getRawType();
關于裸類型與元素類型:
舉個栗子:java.util.List<String>的裸類型是java.util.List,元素類型是:String
ii)如果裸類型是原始類型返回對應的ThriftType,如:DOUBLE,STRING,I32, I64等。
iii)如果是ByteBuffer返回BINARY
iv)如果是Enum生成相應的ThriftEnumMetadata,并據此創建對應的Enum的Thrift類型,具體邏輯略
v)如果是數組,則生成具體類型的數組thrift類型,具體邏輯略
vi)如果是Map,獲取key和value的具體類型,生成相應的thrifttype,在根據這兩thrifttype生成map Thrifttype。
vii)如果是void,返回VOID
viii)如果是結構
ix)如果是Future類型(牛逼,THrift居然有這種東東)
x)其他情況,這時候,將采用隱式轉換類型,來獲取相應的ThriftType。
關于隱式轉換前面已經提到了,系統在初始化時,把DefaultJavaCoercions類作為隱式轉換的初始化對象。主要是一些java原始類型與其包裝類之間的相互轉換。另外在ThriftType中有對應的
這里細節先就先不提了,以后另外專門分一個章節來講解。
4)構建異常信息
5)單方向信息

  • ThriftMethodProcessor類

該類負責執行一個客戶端的Thrift請求,它封裝了該方法的元數據和編解碼器相關信息。

這里先看看他的process方法的主要流程:
1)讀取參數信息
2)讀取消息結束標志
【注意:讀取消息開始,以及讀取消息名,都在前面ThriftServiceProcessor類的process函數中處理過了】
3) 調用方法 : Object response = method.invoke(service, args); 這個很簡單。
在這里,它對于立即返回和Future返回值的函數做了統一處理。

      try {
            Object response = method.invoke(service, args);
            if (response instanceof ListenableFuture) {
                return (ListenableFuture<?>) response;
            }
            return Futures.immediateFuture(response);
        }
        catch (IllegalAccessException | IllegalArgumentException e) {
            // These really should never happen, since the method metadata should have prevented it
            return Futures.immediateFailedFuture(e);
        }
        catch (InvocationTargetException e) {
            Throwable cause = e.getCause();
            if (cause != null) {
                return Futures.immediateFailedFuture(cause);
            }

            return Futures.immediateFailedFuture(e);
        }

4)返回數據
注意,這里有兩塊:成功的和失敗的。
成功的情況下,會返回一個

  • ThriftServer類

負責服務器的相關元素的封裝和服務器的啟動,它主要依賴于NettyServerTransport類來完成服務器的相關功能。

1、首先看看編解碼工廠類

有兩塊:
1)、DEFAULT_FRAME_CODEC_FACTORIES 存放了兩個基于Framed的編解碼:buffered和framed
2)、DEFAULT_PROTOCOL_FACTORIES 存放了兩種協議解析binary和compact,
binary:簡單的二進制編碼
compact:以一種更加緊湊的編碼方式編碼,

注意:Thrift也可以使用Json來編碼,不過Swfit不支持

  • NettyServerTransport類

這是一個核心的通道類,用于解析framed 的thrift消息,分到給指定的TProcessor,然后把返回的消息打包返回都Thrift棧。

1、啟動服務

    public void start(ServerChannelFactory serverChannelFactory)
    {
        bootstrap = new ServerBootstrap(serverChannelFactory);
        bootstrap.setOptions(nettyServerConfig.getBootstrapOptions());
        bootstrap.setPipelineFactory(pipelineFactory);
        serverChannel = bootstrap.bind(new InetSocketAddress(requestedPort));
        InetSocketAddress actualSocket = (InetSocketAddress) serverChannel.getLocalAddress();
        actualPort = actualSocket.getPort();
        Preconditions.checkState(actualPort != 0 && (actualPort == requestedPort || requestedPort == 0));
        log.info("started transport %s:%s", def.getName(), actualPort);
    }

2、協議棧

       this.pipelineFactory = new ChannelPipelineFactory()
        {
            @Override
            public ChannelPipeline getPipeline()
                    throws Exception
            {
                ChannelPipeline cp = Channels.pipeline();
                TProtocolFactory inputProtocolFactory = def.getDuplexProtocolFactory().getInputProtocolFactory();
                NiftySecurityHandlers securityHandlers = def.getSecurityFactory().getSecurityHandlers(def, nettyServerConfig);
                cp.addLast("connectionContext", new ConnectionContextHandler());
                cp.addLast("connectionLimiter", connectionLimiter);
                cp.addLast(ChannelStatistics.NAME, channelStatistics);
                cp.addLast("encryptionHandler", securityHandlers.getEncryptionHandler());
                cp.addLast("frameCodec", def.getThriftFrameCodecFactory().create(def.getMaxFrameSize(),
                                                                                 inputProtocolFactory));
                if (def.getClientIdleTimeout() != null) {
                    // Add handlers to detect idle client connections and disconnect them
                    cp.addLast("idleTimeoutHandler", new IdleStateHandler(nettyServerConfig.getTimer(),
                                                                          def.getClientIdleTimeout().toMillis(),
                                                                          NO_WRITER_IDLE_TIMEOUT,
                                                                          NO_ALL_IDLE_TIMEOUT,
                                                                          TimeUnit.MILLISECONDS));
                    cp.addLast("idleDisconnectHandler", new IdleDisconnectHandler());
                }

                cp.addLast("authHandler", securityHandlers.getAuthenticationHandler());
                cp.addLast("dispatcher", new NiftyDispatcher(def, nettyServerConfig.getTimer()));
                cp.addLast("exceptionLogger", new NiftyExceptionLogger());
                return cp;
            }
        };

1)ConnectionContextHandler 負責在連接建立成功的時候,創建NiftyConnectionContext上線文對象,附著在ctx中【ConnectionHandlerContext】
2)ConnectionLimiter 負責對會話打開和關閉是對連接數進行計數,并判斷是否超過最大連接數,超出的會直接關閉連接。
3)ChannelStatistics 負責統計打開的channel數、上下行流量。
4)安全方面的Handler,這里先不分析了。
5)DefaultThriftFrameCodec,負責Thrift的協議頭的解析。把字節數據變成ThriftMessage對象。從而會觸發到NiftyDispatcher處理器。
對于Framed的消息,Thrift是4個字節的長度+對應長度的消息體。
6)IdleStateHandler用來檢查讀寫或者兩者一起Idle的情況,如果發生了超時現象,就會向上匯報IDLE事件。
7)IdleDisconnectHandler用于處理Idle事件,收到Idle事件后,IdleDisconnectHandler負責關閉連接。
8)認證處理器
9)NiftyDispatcher負責消息的分發
10)NiftyExceptionLogger:負責記錄網絡中的異常數據到日志中。

**NiftyDispatcher類
負責Thrift消息的處理、分發和返回處理。

1、消息處理部分

前面在協議棧已經分析過了,在DefaultThriftFrameCodec處理器處理過后,就會把字節數據變成ThriftMessage,從而觸發NiftyDispatcher的處理,如下所示:

    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
            throws Exception
    {
        if (e.getMessage() instanceof ThriftMessage) {
            ThriftMessage message = (ThriftMessage) e.getMessage();
            message.setProcessStartTimeMillis(System.currentTimeMillis());
            checkResponseOrderingRequirements(ctx, message);

            TNiftyTransport messageTransport = new TNiftyTransport(ctx.getChannel(), message);
            TTransportPair transportPair = TTransportPair.fromSingleTransport(messageTransport);
            TProtocolPair protocolPair = duplexProtocolFactory.getProtocolPair(transportPair);        // 活的雙線協議處理器,這里會根據TThriftServerDef中定義的,缺省值為:this.duplexProtocolFactory = TDuplexProtocolFactory.fromSingleFactory(new TBinaryProtocol.Factory(true, true));

            TProtocol inProtocol = protocolPair.getInputProtocol();
            TProtocol outProtocol = protocolPair.getOutputProtocol();

            processRequest(ctx, message, messageTransport, inProtocol, outProtocol);
        }
        else {
            ctx.sendUpstream(e);
        }
    }

從上面代碼中,協議的出入棧打包器在這里創建了出來,TNiftyTransport類似乎繼承和實現了原生Thrift的TTransport類。(注意看ThriftTransport類的in和out成員,in成員是傳入的TThriftMessage的消息體,而out則是新城成的一個ChannelBuffer)

2、消息分發

NiftyDispatcher的exec成員是一個Executor對象,它負責分發消息執行,收到的ThriftMessage會被放到工作線程池中執行,而不是在io線程中。

           exe.execute(new Runnable() {
                @Override
                public void run() {
                    ListenableFuture<Boolean> processFuture;
                    final AtomicBoolean responseSent = new AtomicBoolean(false);
                    // Use AtomicReference as a generic holder class to be able to mark it final
                    // and pass into inner classes. Since we only use .get() and .set(), we don't
                    // actually do any atomic operations.
                    final AtomicReference<Timeout> expireTimeout = new AtomicReference<>(null);

                    try {
                        try {
                            // 超時處理
                            long timeRemaining = 0;
                            long timeElapsed = System.currentTimeMillis() - message.getProcessStartTimeMillis();
                            if (queueTimeoutMillis > 0) {
                                if (timeElapsed >= queueTimeoutMillis) {
                                    TApplicationException taskTimeoutException = new TApplicationException(
                                            TApplicationException.INTERNAL_ERROR,
                                            "Task stayed on the queue for " + timeElapsed +
                                                    " milliseconds, exceeding configured queue timeout of " + queueTimeoutMillis +
                                                    " milliseconds."
                                    );
                                    sendTApplicationException(taskTimeoutException, ctx, message, requestSequenceId, messageTransport,
                                        inProtocol, outProtocol);
                                    return;
                                }
                            } else if (taskTimeoutMillis > 0) {
                                if (timeElapsed >= taskTimeoutMillis) {
                                    TApplicationException taskTimeoutException = new TApplicationException(
                                            TApplicationException.INTERNAL_ERROR,
                                            "Task stayed on the queue for " + timeElapsed +
                                                    " milliseconds, exceeding configured task timeout of " + taskTimeoutMillis +
                                                    " milliseconds."
                                    );
                                    sendTApplicationException(taskTimeoutException, ctx, message, requestSequenceId, messageTransport,
                                            inProtocol, outProtocol);
                                    return;
                                } else {
                                    timeRemaining = taskTimeoutMillis - timeElapsed;
                                }
                            }

                            if (timeRemaining > 0) {
                                expireTimeout.set(taskTimeoutTimer.newTimeout(new TimerTask() {
                                    @Override
                                    public void run(Timeout timeout) throws Exception {
                                        // The immediateFuture returned by processors isn't cancellable, cancel() and
                                        // isCanceled() always return false. Use a flag to detect task expiration.
                                        if (responseSent.compareAndSet(false, true)) {  // 只能被發送一次返回消息
                                            TApplicationException ex = new TApplicationException(
                                                    TApplicationException.INTERNAL_ERROR,
                                                    "Task timed out while executing."
                                            );
                                            // Create a temporary transport to send the exception
                                            ChannelBuffer duplicateBuffer = message.getBuffer().duplicate();
                                            duplicateBuffer.resetReaderIndex();
                                            TNiftyTransport temporaryTransport = new TNiftyTransport(
                                                    ctx.getChannel(),
                                                    duplicateBuffer,
                                                    message.getTransportType());
                                            TProtocolPair protocolPair = duplexProtocolFactory.getProtocolPair(
                                                    TTransportPair.fromSingleTransport(temporaryTransport));
                                            sendTApplicationException(ex, ctx, message,
                                                    requestSequenceId,
                                                    temporaryTransport,
                                                    protocolPair.getInputProtocol(),
                                                    protocolPair.getOutputProtocol());
                                        }
                                    }
                                }, timeRemaining, TimeUnit.MILLISECONDS));
                            }

                            ConnectionContext connectionContext = ConnectionContexts.getContext(ctx.getChannel());
                            RequestContext requestContext = new NiftyRequestContext(connectionContext, inProtocol, outProtocol, messageTransport);
                            RequestContexts.setCurrentContext(requestContext);
                            processFuture = processorFactory.getProcessor(messageTransport).process(inProtocol, outProtocol, requestContext);
                        } finally {
                            // RequestContext does NOT stay set while we are waiting for the process
                            // future to complete. This is by design because we'll might move on to the
                            // next request using this thread before this one is completed. If you need
                            // the context throughout an asynchronous handler, you need to read and store
                            // it before returning a future.
                            RequestContexts.clearCurrentContext();
                        }

                        Futures.addCallback(
                                processFuture,
                                new FutureCallback<Boolean>() {
                                    @Override
                                    public void onSuccess(Boolean result) {
                                        deleteExpirationTimer(expireTimeout.get());
                                        try {
                                            // Only write response if the client is still there and the task timeout
                                            // hasn't expired.
                                            if (ctx.getChannel().isConnected() && responseSent.compareAndSet(false, true)) {  // 只能被發送一次返回消息,防止跟超時消息搞重
                                                ThriftMessage response = message.getMessageFactory().create(
                                                        messageTransport.getOutputBuffer());
                                                writeResponse(ctx, response, requestSequenceId,
                                                        DispatcherContext.isResponseOrderingRequired(ctx));
                                            }
                                        } catch (Throwable t) {
                                            onDispatchException(ctx, t);
                                        }
                                    }

                                    @Override
                                    public void onFailure(Throwable t) {
                                        deleteExpirationTimer(expireTimeout.get());
                                        onDispatchException(ctx, t);
                                    }
                                }
                        );
                    } catch (TException e) {
                        onDispatchException(ctx, e);
                    }
                }
            });

3、流程說明

1、初始化ThriftServiceProcessor
這個過程充分利用了java的反射原理,基于一個Thrift的服務處理類來構建Thrift服務處理器。
逐步步驟為:
1)生成ThriftCodecManager對象,用于對Thrift協議中的各種數據類型進行編解碼處理。
這個步驟有點特殊,將獨立說明,見:ThriftCodecManager的構建。
2)構建一個服務器處理對象,例子中是一個SwfitScribe対象
3)分析服務器處理対像,提出處理函數,綁定到服務處理器中。

2、協議解析以及消息分發機制

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容