dubbo源碼之啟動(dòng)過程分析

前言

? ? ? ?Apache Dubbo? (incubating)是一款高性能Java RPC框架渡八。在平常業(yè)務(wù)開發(fā)過程中使用的越來越頻繁,同時(shí)也會(huì)遇到更多的問題券盅。這就需要我們更多的了解一下dubbo源碼其障,以便更好的處理問題霉撵。
? ? ??看源碼的話就會(huì)直面一個(gè)棘手的問題:不知道從哪下手浙于,找不到切入點(diǎn)芹助。所以堂湖,本文準(zhǔn)備就dubbo的啟動(dòng)過程做一下宏觀的流程分析,希望對(duì)大家有所幫助周瞎。

問題引入

? ? ??用過dubbo的同學(xué)都知道苗缩,我們只需要在xml文件中配置zk、協(xié)議声诸、要暴露的服務(wù)等信息酱讶,發(fā)布jar包、然后啟動(dòng)spring彼乌。我們的服務(wù)就可以被調(diào)用了泻肯。如下,我們暴露了HelloService慰照。啟動(dòng)spring就可以被遠(yuǎn)程調(diào)用了:

<dubbo:service interface="com.alibaba.dubbo.demo.hello.HelloService" ref="helloService" timeout="300"  ></dubbo:service>

直入主題

? ? ??那么在spring容器啟動(dòng)的過程中灶挟,都做了什么操作才使我們的服務(wù)可以暴露出去呢?為什么dubbo是透明化接入應(yīng)用毒租,對(duì)應(yīng)用沒有任何 API 侵入的呢稚铣?

1.Spring可擴(kuò)展Schema的支持

? ? ??Spring框架從2.0版本開始,提供了基于Schema風(fēng)格的XML擴(kuò)展機(jī)制墅垮,允許開發(fā)者擴(kuò)展最基本的spring配置文件惕医,這樣我們就可以編寫自定義的xml bean解析器然后集成到Spring IoC容器中。
也就是說利用這個(gè)機(jī)制就可以把我們?cè)趚ml文件中配置的東西實(shí)例化成對(duì)象算色。
使用這種機(jī)制需要通過以下幾步:

  1. 設(shè)計(jì)配置屬性和JavaBean
  2. 編寫XSD文件
  3. 編寫NamespaceHandler和BeanDefinitionParser完成解析工作
  4. 編寫spring.handlers和spring.schemas串聯(lián)起所有部件
    接著我們以dubbo的provider為例開始分析
<dubbo:provider registry="test_zk" version="1.0.0" iothreads="300" retries="0"/>

spring啟動(dòng)過程中會(huì)去掃描META-INF/spring.schemas抬伺,拿到dubbo的擴(kuò)展配置,然后根據(jù)配置找到META-INF/dubbo.xsd文件

http\://dubbo.apache.org/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd

至于spring為什么會(huì)掃面META-INF/spring.schemas目錄呢灾梦?答案在PluggableSchemaResolver.java中

public class PluggableSchemaResolver implements EntityResolver {
    public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas";
    private static final Log logger = LogFactory.getLog(PluggableSchemaResolver.class);
    private final ClassLoader classLoader;
    private final String schemaMappingsLocation;
    private volatile Map<String, String> schemaMappings;

    public PluggableSchemaResolver(ClassLoader classLoader) {
        this.classLoader = classLoader;
        this.schemaMappingsLocation = "META-INF/spring.schemas";
    }
}

dubbo.xsd文件中定義了我們Bean的標(biāo)簽峡钓,和Bean中定義的字段一一對(duì)應(yīng)的妓笙;
這一步spring會(huì)把dubbo.xsd文件解析成 Dom 樹,在解析的自定義標(biāo)簽的時(shí)候能岩, spring 會(huì)根據(jù)標(biāo)簽的命名空間和標(biāo)簽名找到一個(gè)解析器寞宫。


Provider

這個(gè)命名空間就是targetNamespace。拿到這個(gè)參數(shù)去掃面META-INF/spring.handlers捧灰。拿到dubbo配置的handler路徑

 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:beans="http://www.springframework.org/schema/beans"
            xmlns:tool="http://www.springframework.org/schema/tool"
            xmlns="http://dubbo.apache.org/schema/dubbo"
            targetNamespace="http://dubbo.apache.org/schema/dubbo">
 http\://dubbo.apache.org/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler

這樣就找到了DubboNamespaceHandler,由該解析器來完成對(duì)該標(biāo)簽內(nèi)容的解析淆九,并返回一個(gè) BeanDefinition 。

public class DubboNamespaceHandler extends NamespaceHandlerSupport {
    public DubboNamespaceHandler() {
    }

    public void init() {
        this.registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        this.registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        this.registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        this.registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        this.registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        this.registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        this.registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        this.registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        this.registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        this.registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
    }

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

在這個(gè)過程中就會(huì)把dubbo自定義的schema配置初始化成Bean對(duì)象毛俏,并維護(hù)在spring容器中炭庙。
(深入了解schema機(jī)制 可參考:https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/xsd-configuration.html

2.spring 事件機(jī)制

dubbo使用到的配置信息都已經(jīng)托管在spring容器中了,服務(wù)又是怎么暴露的呢煌寇?萬事俱別焕蹄,只欠東風(fēng),此時(shí)就需要一個(gè)觸發(fā)dubbo服務(wù)啟動(dòng)的事件阀溶。

因?yàn)槭呛蛃pring整合的腻脏,我們就直接定位到dubbo-config-spring目錄下,定位發(fā)現(xiàn)一個(gè)類ServiceBean银锻。其實(shí)如果仔細(xì)留意一下dubbo的啟動(dòng)日志,通過文本搜索也是可以快速定位到這個(gè)類的永品。
結(jié)構(gòu)圖
日志.png
看一下它的繼承體系,它繼承了ApplicationListener.這個(gè)就是spring的事件機(jī)制,spring容器初始化完成之后就會(huì)觸發(fā)ServiceBean的onApplicationEvent方法击纬。這個(gè)就是整個(gè)dubbo服務(wù)啟動(dòng)的入口了鼎姐。
繼承體系
public void onApplicationEvent(ApplicationEvent event) {
        if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
            if (isDelay() && ! isExported() && ! isUnexported()) {
                if (logger.isInfoEnabled()) {
                    logger.info("The service ready on spring started. service: " + getInterface());
                }
                export();
            }
        }
    }

3.服務(wù)暴露

從export()方法開始,才真正進(jìn)入了dubbo的服務(wù)暴露流程更振,在這個(gè)過程中就會(huì)涉及到多協(xié)議暴露服務(wù)炕桨、注冊(cè)zk、暴露本地和遠(yuǎn)程服務(wù)肯腕,獲取invoker献宫,將invoker轉(zhuǎn)化成exporter等一系列操作。如同官方提供的那樣:


服務(wù)暴露

接著會(huì)到ServiceConfig.export()方法实撒,這里面涉及到dubbo服務(wù)延遲暴露的一個(gè)點(diǎn)姊途,delay這個(gè)參數(shù)可以配置在<dubbo:provider/> 或者<dubbo:service/>中,目的是為了延遲注冊(cè)服務(wù)時(shí)間(毫秒) 知态,設(shè)為-1時(shí)捷兰,表示延遲到Spring容器初始化完成時(shí)暴露服務(wù)。一些特殊的場(chǎng)景肴甸,可以通過修改該參數(shù)來解決服務(wù)剛啟動(dòng)接口響應(yīng)較慢的案例寂殉。
delay.png

ServiceConfig.doExport()主要是做一些合法性的校驗(yàn)工作:
  1. application&registry&protocol等有效性檢查囚巴;
  2. <dubbo:service>中配置的interface合法性檢查:接口不能為空,檢查接口類型必需為接口,檢查方法是否在接口中存在.(checkInterfaceAndMethods)
  3. 檢查xml配置中interface和ref是否匹配(interfaceClass.isInstance(ref))
  4. 有效性檢查通過后原在,調(diào)用doExportUrls()發(fā)布dubbo服務(wù)友扰;
    在ServiceConfig.doExportUrls()方法,這里會(huì)進(jìn)行多協(xié)議暴露服務(wù)庶柿,由于dubbo不僅支持dubbo協(xié)議同時(shí)還支持http村怪、webservice、thrift等協(xié)議浮庐。如果我們配置的service需要同時(shí)提供多種服務(wù)甚负,那么會(huì)根據(jù)不同的協(xié)議進(jìn)行循環(huán)暴露。
<dubbo:service interface="com.alibaba.dubbo.demo.hello.HelloService" ref="helloService" timeout="300" protocol="dubbo"></dubbo:service>  
<dubbo:service interface="com.alibaba.dubbo.demo.hello.HelloService" ref="helloService" timeout="300" protocol="http"></dubbo:service>
protocol.png

在doExportUrlsFor1Protocol中會(huì)把所有的相關(guān)屬性封裝到Map中审残,構(gòu)造dubbo定義的統(tǒng)一數(shù)據(jù)模型URL,這個(gè)url會(huì)貫穿服務(wù)暴露和調(diào)用的整個(gè)流程梭域。

 URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);

接著是根據(jù)參數(shù)scope判斷服務(wù)的發(fā)布范圍。服務(wù)的發(fā)布范圍分為不暴露搅轿、本地暴露病涨、遠(yuǎn)程暴露。
scope的配置規(guī)則如下:

  1. 如果配置scope=none,不發(fā)布這個(gè)dubbo服務(wù)璧坟;
  2. 如果配置scope=local既穆,只本地暴露這個(gè)dubbo服務(wù);
  3. 如果配置remote雀鹃,只遠(yuǎn)程暴露這個(gè)dubbo服務(wù)
  4. 如果不配置或者不為以上三種幻工,既暴露本地服務(wù),又暴露遠(yuǎn)程服務(wù)黎茎;
       //配置為none不暴露
        if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
            if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
                exportLocal(url);
            }
            if (! Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope) ){
                if (logger.isInfoEnabled()) {
                    logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                }
                if (registryURLs != null && registryURLs.size() > 0
                        && url.getParameter("register", true)) {
                    for (URL registryURL : registryURLs) {
                        url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
                        URL monitorUrl = loadMonitor(registryURL);
                        if (monitorUrl != null) {
                            url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
                        }
                        if (logger.isInfoEnabled()) {
                            logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                        }
                        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));

                        Exporter<?> exporter = protocol.export(invoker);
                        exporters.add(exporter);
                    }
                } else {
                    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);

                    Exporter<?> exporter = protocol.export(invoker);
                    exporters.add(exporter);
                }
            }
        }

那么為什么會(huì)有本地暴露呢?因?yàn)樵赿ubbo中我們一個(gè)服務(wù)可能既是Provider,又是Consumer,因此就存在他自己調(diào)用自己服務(wù)的情況,如果再通過網(wǎng)絡(luò)去訪問囊颅,就會(huì)白白增加一層網(wǎng)絡(luò)開銷。所以本地暴露和遠(yuǎn)程暴露的區(qū)別如下:

  1. 本地暴露是暴露在JVM中,不需要網(wǎng)絡(luò)通信.
  2. 遠(yuǎn)程暴露是將ip,端口等信息暴露給遠(yuǎn)程客戶端,調(diào)用時(shí)需要網(wǎng)絡(luò)通信.

    本地暴露服務(wù)的時(shí)候url是以injvm開頭的工三,而遠(yuǎn)程服務(wù)是以registry開頭的迁酸,如圖示:
    injvm.png

    registry.png

    上面代碼也可以看出來,本地暴露和遠(yuǎn)程暴露的本質(zhì)都是是通過把拼裝好的url轉(zhuǎn)換成invoker俭正,然后把invoker轉(zhuǎn)換為exporter奸鬓。
    點(diǎn)開getInvoker方法

 /**
     * create invoker.
     * 
     * @param <T>
     * @param proxy
     * @param type
     * @param url
     * @return invoker
     */
    @Adaptive({Constants.PROXY_KEY})
    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;

這里用到了Adaptive,就會(huì)生成動(dòng)態(tài)編譯的Adaptive類掸读。這個(gè)類就是getInvoker方法的具體實(shí)現(xiàn)串远。
拿到invoker之后,調(diào)用protocol.export(invoker)把invoker轉(zhuǎn)換成exporter儿惫。

 /**
     * 暴露遠(yuǎn)程服務(wù):<br>
     * 1. 協(xié)議在接收請(qǐng)求時(shí)澡罚,應(yīng)記錄請(qǐng)求來源方地址信息:RpcContext.getContext().setRemoteAddress();<br>
     * 2. export()必須是冪等的,也就是暴露同一個(gè)URL的Invoker兩次肾请,和暴露一次沒有區(qū)別留搔。<br>
     * 3. export()傳入的Invoker由框架實(shí)現(xiàn)并傳入,協(xié)議不需要關(guān)心铛铁。<br>
     * 
     * @param <T> 服務(wù)的類型
     * @param invoker 服務(wù)的執(zhí)行體
     * @return exporter 暴露服務(wù)的引用隔显,用于取消暴露
     * @throws RpcException 當(dāng)暴露服務(wù)出錯(cuò)時(shí)拋出却妨,比如端口已占用
     */
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

到這里就是服務(wù)暴露的總流程。

4.netty服務(wù)啟動(dòng)

在invoker->exporter轉(zhuǎn)換的過程中又涉及到了dubbo連接池的創(chuàng)建和netty的初始化括眠。

定位到了DubboProtocol.export()方法彪标,接著會(huì)調(diào)用openServer(url) -> createServer(url)
DubboProtocol
下圖openServer的key就是ip:port
openServer

createServer

在createServer方法中利用dubbo SPI機(jī)制找到NettyTransporter,new NettyServer()->doOpen().最終我們就看到boss 線程掷豺,worker 線程捞烟,和 ServerBootstrap。


NettyTransporter

doOpen
netty
到此当船,netty開始進(jìn)行初始化,并指定了handler為nettyHandler,然后調(diào)用 bind 方法题画,完成端口的綁定,開啟端口監(jiān)聽;
而 Client 在 Spring getBean 的時(shí)候德频,會(huì)創(chuàng)建 Client.當(dāng)調(diào)用遠(yuǎn)程方法的時(shí)候婴程,將數(shù)據(jù)通過 dubbo 協(xié)議編碼發(fā)送到 NettyServer,然后 NettServer 收到數(shù)據(jù)后解碼抱婉,并調(diào)用本地方法档叔,并返回?cái)?shù)據(jù),完成一次RPC 調(diào)用蒸绩。
final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);

NettyHandler類它繼承了netty框架的SimpleChannelHandler類衙四,重寫了messageReceived方法。接收到客戶端請(qǐng)求的入口就是messageReceived方法

  @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
        try {
            handler.received(channel, e.getMessage());
        } finally {
            NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
        }
    }

執(zhí)行了handler的received方法患亿,這個(gè)handler其實(shí)就是DubboProtocol中的requestHandler传蹈,因?yàn)樵趩?dòng)netty服務(wù)的時(shí)候,就將requestHandler對(duì)象一直傳遞到了NettyServer步藕,再通過NettyServer類的構(gòu)造函數(shù)將它保存到了NettyServer類的終極父類AbstractPeer的handler屬性上惦界,AbstractPeer類又實(shí)現(xiàn)了ChannelHandler接口,重寫了received方法咙冗。
所以當(dāng)netty框架接收到請(qǐng)求時(shí)執(zhí)行messageReceived方法里面的handler.received(channel, e.getMessage()); 沾歪,其實(shí)執(zhí)行的是AbstractPeer類的received方法,received然后里面又執(zhí)行了handler.received(ch, msg); 然后received中又調(diào)用了reply方法雾消;
在reply方法中灾搏,完成了數(shù)據(jù)的解碼,和合法性校驗(yàn)立润。最終調(diào)用本地方法狂窑,返回?cái)?shù)據(jù),完成一次RPC 調(diào)用桑腮。

private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
        
        public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
            if (message instanceof Invocation) {
                Invocation inv = (Invocation) message;
                Invoker<?> invoker = getInvoker(channel, inv);
                //如果是callback 需要處理高版本調(diào)用低版本的問題
                if (Boolean.TRUE.toString().equals(inv.getAttachments().get(IS_CALLBACK_SERVICE_INVOKE))){
                    String methodsStr = invoker.getUrl().getParameters().get("methods");
                    boolean hasMethod = false;
                    if (methodsStr == null || methodsStr.indexOf(",") == -1){
                        hasMethod = inv.getMethodName().equals(methodsStr);
                    } else {
                        String[] methods = methodsStr.split(",");
                        for (String method : methods){
                            if (inv.getMethodName().equals(method)){
                                hasMethod = true;
                                break;
                            }
                        }
                    }
                    if (!hasMethod){
                        logger.warn(new IllegalStateException("The methodName "+inv.getMethodName()+" not found in callback service interface ,invoke will be ignored. please update the api interface. url is:" + invoker.getUrl()) +" ,invocation is :"+inv );
                        return null;
                    }
                }
                RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
                return invoker.invoke(inv);
            }
            throw new RemotingException(channel, "Unsupported request: " + message == null ? null : (message.getClass().getName() + ": " + message) + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
        }

        @Override
        public void received(Channel channel, Object message) throws RemotingException {
            if (message instanceof Invocation) {
                reply((ExchangeChannel) channel, message);
            } else {
                super.received(channel, message);
            }
        }

最后

? ? ? ?如圖泉哈,dubbo的的模型十分易懂,但涉及到的東西確實(shí)很多。以上只是對(duì)第一步:0.start 做了一個(gè)簡單的流水賬分析丛晦。
? ? ? 所以巨缘,本文只是想做個(gè)引子,更多的細(xì)節(jié)還需要靠大家去挖掘采呐。剩下的只有去debug the universe了。

Architecture.png

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末搁骑,一起剝皮案震驚了整個(gè)濱河市斧吐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌仲器,老刑警劉巖煤率,帶你破解...
    沈念sama閱讀 221,406評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異乏冀,居然都是意外死亡蝶糯,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門辆沦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來昼捍,“玉大人,你說我怎么就攤上這事肢扯《什纾” “怎么了?”我有些...
    開封第一講書人閱讀 167,815評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵蔚晨,是天一觀的道長乍钻。 經(jīng)常有香客問我,道長铭腕,這世上最難降的妖魔是什么银择? 我笑而不...
    開封第一講書人閱讀 59,537評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮累舷,結(jié)果婚禮上浩考,老公的妹妹穿的比我還像新娘。我一直安慰自己被盈,他們只是感情好怀挠,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著害捕,像睡著了一般绿淋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上尝盼,一...
    開封第一講書人閱讀 52,184評(píng)論 1 308
  • 那天吞滞,我揣著相機(jī)與錄音,去河邊找鬼。 笑死裁赠,一個(gè)胖子當(dāng)著我的面吹牛殿漠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播佩捞,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼绞幌,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了一忱?” 一聲冷哼從身側(cè)響起莲蜘,我...
    開封第一講書人閱讀 39,668評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎帘营,沒想到半個(gè)月后票渠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,212評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芬迄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評(píng)論 3 340
  • 正文 我和宋清朗相戀三年问顷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片禀梳。...
    茶點(diǎn)故事閱讀 40,438評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡杜窄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出算途,到底是詐尸還是另有隱情羞芍,我是刑警寧澤,帶...
    沈念sama閱讀 36,128評(píng)論 5 349
  • 正文 年R本政府宣布郊艘,位于F島的核電站荷科,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏纱注。R本人自食惡果不足惜畏浆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望狞贱。 院中可真熱鬧刻获,春花似錦、人聲如沸瞎嬉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽氧枣。三九已至沐兵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間便监,已是汗流浹背扎谎。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評(píng)論 1 272
  • 我被黑心中介騙來泰國打工碳想, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人毁靶。 一個(gè)月前我還...
    沈念sama閱讀 48,827評(píng)論 3 376
  • 正文 我出身青樓胧奔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親预吆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子龙填,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評(píng)論 2 359

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)拐叉,斷路器岩遗,智...
    卡卡羅2017閱讀 134,695評(píng)論 18 139
  • dubbo.xsd 文件說明 當(dāng)我們要使用Dubbo進(jìn)行 配置時(shí),需要像下面那樣引入dubbo.xsd這個(gè)文件. ...
    Mis_Gtw閱讀 1,104評(píng)論 0 1
  • 先看官網(wǎng)兩張圖【引用來自官網(wǎng)】:image.png 官網(wǎng)說明: 1.首先 ServiceConfig 類拿到對(duì)外提...
    致慮閱讀 1,439評(píng)論 1 4
  • 先看官網(wǎng)兩張圖【引用來自官網(wǎng)】:image.png 官網(wǎng)說明: 1.首先 ReferenceConfig 類的 i...
    致慮閱讀 1,028評(píng)論 0 2
  • 1. 簡介 我們分析服務(wù)引用的原理巷嚣。在 Dubbo 中,我們可以通過兩種方式引用遠(yuǎn)程服務(wù)钳吟。第一種是使用服務(wù)直聯(lián)的方...
    Java黎先生閱讀 736評(píng)論 1 0