小白談分布式數(shù)據(jù)庫(kù)設(shè)計(jì)3——外部rpc模塊設(shè)計(jì)

client訪問(wèn)rpc設(shè)計(jì)

最簡(jiǎn)單的rpc設(shè)計(jì),僅需一小段代碼就可以完成续室。性能的主要消耗是網(wǎng)絡(luò)通信與序列化和反序列化挪略。這里我們?cè)O(shè)計(jì)一個(gè)較為通用的rpc框架推掸,可以完成java下的任意遠(yuǎn)程調(diào)用。
我們選擇netty來(lái)做網(wǎng)絡(luò)通信声邦,暫時(shí)選擇三種可用的java序列化方式hessian,kyro和jdk自己的序列化乏奥。

通信數(shù)據(jù)

根據(jù)請(qǐng)求的類名、方法名亥曹、方法參數(shù)邓了,以及接口名,就可以根據(jù)反射和代理來(lái)處理請(qǐng)求媳瞪。

public class MessageRequest implements Serializable {

    private static final long serialVersionUID = 779639215038924077L;
    private String messageId;   //唯一ID
    private String className;   //接口名
    private String methodName;  //方法名
    private Class<?>[] typeParameters;  //參數(shù)類型
    private Object[] parametersVal;     //參數(shù)

    public String getMessageId() {
        return messageId;
    }

    public void setMessageId(String messageId) {
        this.messageId = messageId;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Class<?>[] getTypeParameters() {
        return typeParameters;
    }

    public void setTypeParameters(Class<?>[] typeParameters) {
        this.typeParameters = typeParameters;
    }

    public Object[] getParameters() {
        return parametersVal;
    }

    public void setParameters(Object[] parametersVal) {
        this.parametersVal = parametersVal;
    }

    @Override
    public String toString() {
        return "MessageRequest{" +
                "messageId='" + messageId + '\'' +
                ", className='" + className + '\'' +
                ", methodName='" + methodName + '\'' +
                ", typeParameters=" + Arrays.toString(typeParameters) +
                ", parametersVal=" + Arrays.toString(parametersVal) +
                '}';
    }
}
public class MessageResponse implements Serializable {
    private static final long serialVersionUID = -4628239730293658445L;
    private String messageId;   //唯一ID
    private String error;       //錯(cuò)誤消息
    private Object resultDesc;  //方法調(diào)用結(jié)果

    public String getMessageId() {
        return messageId;
    }

    public void setMessageId(String messageId) {
        this.messageId = messageId;
    }

    public String getError() {
        return error;
    }

    public void setError(String error) {
        this.error = error;
    }

    public Object getResult() {
        return resultDesc;
    }

    public void setResult(Object resultDesc) {
        this.resultDesc = resultDesc;
    }

    @Override
    public String toString() {
        return "MessageResponse{" +
                "messageId='" + messageId + '\'' +
                ", error='" + error + '\'' +
                ", resultDesc=" + resultDesc +
                '}';
    }
}
public class MessageCallBack {
    private MessageResponse response;
    private Lock lock = new ReentrantLock();
    private Condition finish = lock.newCondition();

    public Object start() throws InterruptedException {
        try {
            lock.lock();
            finish.await(10 * 1000, TimeUnit.MILLISECONDS);
            if (this.response != null) {
                return this.response.getResult();
            } else {
                return null;
            }
        }finally {
            lock.unlock();
        }
    }

    public void over(MessageResponse reponse) {
        try {
            lock.lock();
            finish.signal();
            this.response = reponse;
        } finally {
            lock.unlock();
        }
    }
}

序列化與反序列化

類的encode和decode,設(shè)計(jì)一個(gè)接口

public interface MessageCodecUtil {
    int MESSAGE_LENGTH = 4;

    void encode(final ByteBuf out, final Object message) throws IOException;

    Object decode(byte[] body) throws IOException;
}

encode和decode都需要用到序列化和反序列化骗炉,再設(shè)計(jì)一個(gè)接口

public interface RpcSerialize {
    void serialize(OutputStream output, Object object) throws IOException;

    Object deserialize(InputStream input) throws IOException;
}

netty的childHandler需要pipeline添加一系列處理,再設(shè)計(jì)一個(gè)接口

public interface SerializeFrame {
    void select(ChannelPipeline pipeline, Map<String, Object> handlerMap);
}

因?yàn)橛卸喾N序列化方式可以選擇蛇受,這里我們使用策略模式句葵,不同的策略對(duì)應(yīng)不同的序列化和encode方式

public class SerializeContext {
    private SerializeFrame serializeFrame;

    public SerializeContext(SerializeFrame serializeFrame) {
        this.serializeFrame = serializeFrame;
    }

    public void setSerializeFrame(SerializeFrame serializeFrame) {
        this.serializeFrame = serializeFrame;
    }

    public void select(ChannelPipeline pipeline, Map<String,Object> handlerMap) {
        serializeFrame.select(pipeline,handlerMap);
    }
}

具體實(shí)現(xiàn)就不貼代碼了,只是在序列化對(duì)象構(gòu)造時(shí)使用倒了對(duì)象池化技術(shù)兢仰,kyro是有自己的KryoPool的乍丈,hessian就需要使用common-pool2來(lái)池化對(duì)象。
另外把将,服務(wù)端還需要定義一個(gè)注解轻专,用來(lái)確認(rèn)對(duì)應(yīng)的服務(wù)端實(shí)現(xiàn)類

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface RpcServer {
    Class<?> value();
}

對(duì)于這個(gè)注解的處理,我們?cè)诜?wù)起來(lái)的時(shí)候模仿spring依賴注入的方式察蹲,將所有的service先實(shí)例化

public class RpcAnnotationFactory {
    public static Map<Annotation,Object> getBeansWithAnnotation(Class<? extends Annotation> annotation,String packageName){
        Map<Annotation,Object> handlerMap = new ConcurrentHashMap<>();
        String packageDirName = packageName.replace('.', '/');
        Enumeration<URL> dirs;
        try {
            dirs = Thread.currentThread().getContextClassLoader().getResources(
                    packageDirName);
            while (dirs.hasMoreElements()){
                URL url = dirs.nextElement();
                String protocol = url.getProtocol();
                if ("file".equals(protocol)) {
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    findAndAddClassesInPackageByFile(annotation, packageName, filePath,
                            handlerMap);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return handlerMap;
    }

    private static void findAndAddClassesInPackageByFile(Class<? extends Annotation> annotation, String packageName, String packagePath, Map<Annotation, Object> handlerMap){
        File dir = new File(packagePath);
        if (!dir.exists() || !dir.isDirectory()) {
            return;
        }
        File[] dirfiles = dir.listFiles(new FileFilter(){
            @Override
            public boolean accept(File pathname) {
                return pathname.isDirectory() || pathname.getName().endsWith(".class");
            }
        });

        for (File file : dirfiles){
            if (file.isDirectory()) {
                findAndAddClassesInPackageByFile(annotation,packageName + "."
                                + file.getName(), file.getAbsolutePath(),
                        handlerMap);
            }else{
                String className = file.getName().substring(0, file.getName().length() - 6);
                Object serviceBean = null;
                try {
                    Class clazz = Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className);
                    Annotation anno = clazz.getAnnotation(annotation);
                    if(anno != null){
                        serviceBean = clazz.newInstance();
                        handlerMap.put(anno,serviceBean);
                    }

                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

在MessageRecvExecutor初始化的時(shí)候请垛,實(shí)現(xiàn)依賴注入.這里還是用了google的ListeningExecutorService來(lái)做異步

public class MessageRecvExecutor {
    private static Logger LOG = Logger.getLogger(MessageRecvExecutor.class);
    private String serverAddress;

    private SerializeFrame serializeFrame;

    private final static String DELIMITER = ":";

    private Map<String, Object> handlerMap = new ConcurrentHashMap<>();

    private static ListeningExecutorService threadPoolExecutor;

    public MessageRecvExecutor(String serverAddress, SerializeFrame serializeFrame) {
        this.serverAddress = serverAddress;
        this.serializeFrame = serializeFrame;
    }

    public static void submit(Callable<Boolean> task, final ChannelHandlerContext context, final MessageRequest request,  final MessageResponse response){
        if (threadPoolExecutor == null) {
            synchronized (MessageRecvExecutor.class) {
                if (threadPoolExecutor == null) {
                    threadPoolExecutor = MoreExecutors.listeningDecorator((ThreadPoolExecutor) RpcThreadPool.getExecutor(16, -1));
                }
            }
        }

        ListenableFuture<Boolean> listenableFuture = threadPoolExecutor.submit(task);
        Futures.addCallback(listenableFuture, new FutureCallback<Boolean>() {
            @Override
            public void onSuccess(@Nullable Boolean aBoolean) {
                context.writeAndFlush(response).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        LOG.info("RPC Server Send message-id respone:" + request.getMessageId());
                    }
                });
            }

            @Override
            public void onFailure(Throwable throwable) {
                throwable.printStackTrace();
            }
        }, threadPoolExecutor);
    }

    public void init(String packageName){
        Map<Annotation, Object> rpcServiceObject = RpcAnnotationFactory.getBeansWithAnnotation(RpcServer.class,packageName);

        if(MapUtils.isNotEmpty(rpcServiceObject)) {
            for(Object serviceBean : rpcServiceObject.values()) {
                String interfaceName = serviceBean.getClass().getAnnotation(RpcServer.class).value().getName();
                handlerMap.put(interfaceName, serviceBean);
            }
        }
    }

    public void afterPropertiesSet(){
        ThreadFactory threadRpcFactory = new NamedThreadFactory("NettyRPC ThreadFactory");
        int parallel = Runtime.getRuntime().availableProcessors() * 2;
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup(parallel, threadRpcFactory, SelectorProvider.provider());
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(boss, worker).channel(NioServerSocketChannel.class)
                .childHandler(new MessageRecvChannelInitializer(handlerMap).buildRpcSerializeProtocol(new SerializeContext(serializeFrame)))
                .option(ChannelOption.SO_BACKLOG, 128)
                .childOption(ChannelOption.SO_KEEPALIVE, true);
        String[] ipAddr = serverAddress.split(MessageRecvExecutor.DELIMITER);
        if(ipAddr.length == 2){
            String host = ipAddr[0];
            int port = Integer.parseInt(ipAddr[1]);
            try {
                ChannelFuture future = bootstrap.bind(host, port).sync();
                LOG.info(String.format("Netty RPC Server start success!\nip:%s\nport:%d\nprotocol:%s\n\n", host, port, new SerializeContext(serializeFrame).toString()));
                future.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                worker.shutdownGracefully();
                boss.shutdownGracefully();
            }

        }else {
            LOG.error("Netty RPC Server start fail!\n");
        }
    }

}

server端具體的計(jì)算是放在一個(gè)Callable里面,使用反射來(lái)進(jìn)行

public class MessageRecvInitializeTask implements Callable<Boolean> {
    private MessageRequest request = null;
    private MessageResponse response = null;
    private Map<String, Object> handlerMap = null;

    public MessageResponse getResponse() {
        return response;
    }

    public MessageRequest getRequest() {
        return request;
    }

    public void setRequest(MessageRequest request) {
        this.request = request;
    }

    MessageRecvInitializeTask(MessageRequest request, MessageResponse response, Map<String, Object> handlerMap) {
        this.request = request;
        this.response = response;
        this.handlerMap = handlerMap;
    }

    @Override
    public Boolean call() throws Exception {
        response.setMessageId(request.getMessageId());
        try {
            Object result = reflect(request);
            response.setResult(result);
            return Boolean.TRUE;
        } catch (Throwable t) {
            response.setError(t.toString());
            t.printStackTrace();
            return Boolean.FALSE;
        }
    }

    private Object reflect(MessageRequest request) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        String className = request.getClassName();
        Object serviceBean = handlerMap.get(className);
        String methodName = request.getMethodName();
        Object[] parameters = request.getParameters();
        return MethodUtils.invokeMethod(serviceBean, methodName, parameters);
    }
}

具體的遠(yuǎn)程調(diào)用方法需要約定相同的接口和實(shí)現(xiàn)洽议,然后client端封裝MessageSendExecutor來(lái)進(jìn)行調(diào)用叼屠。client端主要也是使用netty將MessageRequest發(fā)送,并將放回的消息序列化為MessageResponse绞铃。

public class RpcServerLoader {
    private volatile static RpcServerLoader rpcServerLoader;
    private final static String DELIMITER = ":";

    private final static int parallel = Math.max(2,Runtime.getRuntime().availableProcessors() * 2);

    private EventLoopGroup eventLoopGroup = new NioEventLoopGroup(parallel);

    private static ListeningExecutorService threadPoolExecutor = MoreExecutors.listeningDecorator((ThreadPoolExecutor) RpcThreadPool.getExecutor(16, -1));
    private MessageSendHandler messageSendHandler;
    private Lock lock = new ReentrantLock();
    private Condition connectStatus = lock.newCondition();
    private Condition handlerStatus = lock.newCondition();

    private RpcServerLoader() {
    }

    public static RpcServerLoader getInstance() {
        if (rpcServerLoader == null) {
            synchronized (RpcServerLoader.class) {
                if (rpcServerLoader == null) {
                    rpcServerLoader = new RpcServerLoader();
                }
            }
        }
        return rpcServerLoader;
    }

    public void load(String serverAddress, SerializeFrame serializeFrame) {
        String[] ipAddr = serverAddress.split(RpcServerLoader.DELIMITER);
        if (ipAddr.length == 2) {
            String host = ipAddr[0];
            int port = Integer.parseInt(ipAddr[1]);
            final InetSocketAddress remoteAddr = new InetSocketAddress(host, port);
            ListenableFuture<Boolean> listenableFuture = threadPoolExecutor.submit(new MessageSendInitializeTask(eventLoopGroup, remoteAddr, serializeFrame));
            Futures.addCallback(listenableFuture, new FutureCallback<Boolean>() {
                @Override
                public void onSuccess(@Nullable Boolean aBoolean) {
                    try {
                        lock.lock();
                        if (messageSendHandler == null) handlerStatus.await();
                        if (aBoolean == Boolean.TRUE && messageSendHandler != null) connectStatus.signalAll();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }

                }

                @Override
                public void onFailure(Throwable throwable) {
                    throwable.printStackTrace();
                }
            }, threadPoolExecutor);
        }
    }

    public void setMessageSendHandler(MessageSendHandler messageInHandler) {
        try {
            lock.lock();
            this.messageSendHandler = messageInHandler;
            handlerStatus.signal();
        } finally {
            lock.unlock();
        }
    }

    public MessageSendHandler getMessageSendHandler() throws InterruptedException {
        try {
            lock.lock();
            if (messageSendHandler == null) {
                connectStatus.await();
            }
            return messageSendHandler;
        } finally {
            lock.unlock();
        }
    }

    public void unLoad() {
        messageSendHandler.close();
        threadPoolExecutor.shutdown();
        eventLoopGroup.shutdownGracefully();
    }
}

client提交任務(wù)Callable

public class MessageSendInitializeTask implements Callable<Boolean> {
    private EventLoopGroup eventLoopGroup = null;
    private InetSocketAddress serverAddress;
    private SerializeFrame serializeFrame;

    public MessageSendInitializeTask(EventLoopGroup eventLoopGroup, InetSocketAddress serverAddress, SerializeFrame serializeFrame) {
        this.eventLoopGroup = eventLoopGroup;
        this.serverAddress = serverAddress;
        this.serializeFrame = serializeFrame;
    }

    @Override
    public Boolean call() throws Exception {
        Bootstrap b = new Bootstrap();
        b.group(eventLoopGroup).channel(NioSocketChannel.class).option(ChannelOption.SO_KEEPALIVE, true);
        b.handler(new MessageSendChannelInitializer().buildRpcSerializeProtocol(new SerializeContext(serializeFrame)));
        ChannelFuture future = b.connect(serverAddress);
        future.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if(future.isSuccess()){
                    MessageSendHandler handler = future.channel().pipeline().get(MessageSendHandler.class);
                    RpcServerLoader.getInstance().setMessageSendHandler(handler);
                }
            }
        });

        return Boolean.TRUE;
    }
}

MessageSendHandler主要用來(lái)處理返回?cái)?shù)據(jù)MessageResponse

public class MessageSendHandler extends ChannelInboundHandlerAdapter {
    private ConcurrentHashMap<String, MessageCallBack> mapCallBack = new ConcurrentHashMap<>();
    private volatile Channel channel;
    private SocketAddress remoteAddr;
    public Channel getChannel() {
        return channel;
    }

    public SocketAddress getRemoteAddr() {
        return remoteAddr;
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
        this.channel = ctx.channel();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        this.remoteAddr = this.channel.remoteAddress();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        MessageResponse response = (MessageResponse) msg;
        String messageId = response.getMessageId();
        MessageCallBack callBack = mapCallBack.get(messageId);
        if (callBack != null) {
            mapCallBack.remove(messageId);
            callBack.over(response);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }

    public void close() {
        channel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
    }

    public MessageCallBack sendRequest(MessageRequest request){
        MessageCallBack callBack = new MessageCallBack();
        mapCallBack.put(request.getMessageId(), callBack);
        channel.writeAndFlush(request);
        return callBack;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末镜雨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌荚坞,老刑警劉巖挑宠,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異颓影,居然都是意外死亡各淀,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門诡挂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)碎浇,“玉大人,你說(shuō)我怎么就攤上這事璃俗∨В” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵城豁,是天一觀的道長(zhǎng)苟穆。 經(jīng)常有香客問(wèn)我,道長(zhǎng)唱星,這世上最難降的妖魔是什么雳旅? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮间聊,結(jié)果婚禮上攒盈,老公的妹妹穿的比我還像新娘。我一直安慰自己哎榴,他們只是感情好沦童,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著叹话,像睡著了一般偷遗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上驼壶,一...
    開(kāi)封第一講書(shū)人閱讀 51,727評(píng)論 1 305
  • 那天氏豌,我揣著相機(jī)與錄音,去河邊找鬼热凹。 笑死泵喘,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的般妙。 我是一名探鬼主播纪铺,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼碟渺!你這毒婦竟也來(lái)了鲜锚?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎芜繁,沒(méi)想到半個(gè)月后旺隙,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡骏令,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年蔬捷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榔袋。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡周拐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出凰兑,到底是詐尸還是另有隱情妥粟,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布聪黎,位于F島的核電站,受9級(jí)特大地震影響备恤,放射性物質(zhì)發(fā)生泄漏稿饰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一露泊、第九天 我趴在偏房一處隱蔽的房頂上張望喉镰。 院中可真熱鬧,春花似錦惭笑、人聲如沸侣姆。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)捺宗。三九已至,卻和暖如春川蒙,著一層夾襖步出監(jiān)牢的瞬間蚜厉,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工畜眨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留昼牛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓康聂,卻偏偏與公主長(zhǎng)得像贰健,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子恬汁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理伶椿,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,668評(píng)論 18 139
  • 轉(zhuǎn)自:http://blog.csdn.net/kesonyk/article/details/50924489 ...
    晴天哥_王志閱讀 24,813評(píng)論 2 38
  • RPC框架遠(yuǎn)程調(diào)用的實(shí)現(xiàn)方式在原理上是比較簡(jiǎn)單的悬垃,即將調(diào)用的方法(接口名游昼、方法名、參數(shù)類型尝蠕、參數(shù))序列化之后發(fā)送到...
    謎碌小孩閱讀 3,105評(píng)論 0 13
  • 我在想可能是完了烘豌,失望,看明白了看彼,最終還是沒(méi)有可以信賴的線索廊佩,
    位子閱讀 352評(píng)論 0 1
  • 文/王寧子 又一年在腳下走到終點(diǎn),不想下車靖榕,很想拽著歲月的尾巴尖兒蕩個(gè)秋千标锄,用盡力氣蕩回去,然后心安理得地賴在春天...
    王寧子閱讀 596評(píng)論 27 21