1. 程式人生 > >dubbo原始碼:dubbo服務暴露過程

dubbo原始碼:dubbo服務暴露過程

  1. dubbo標籤解析

    spring通過在DubboNamespaceHandler註冊dubbo解析器DubboBeanDefinitionParser,在載入Bean的時候同時解析dubbo標籤並載入dubbo標籤解析後的Bean

  2. 暴露dubbo服務

    dubbo標籤解析完成後,會把dubbo:service標籤解析解析成型別為ServiceBean的RootBeanDefinition,並進行例項化,

    <!--定義了提供方應用資訊,用於計算依賴關係;在 dubbo-admin 或 dubbo-monitor 會顯示這個名字,方便辨識-->
        <dubbo:application
    name="debbo-provider" owner="programmer" organization="dubbox"/>
    <!--使用 zookeeper 註冊中心暴露服務,注意要先開啟 zookeeper--> <dubbo:registry address="zookeeper://localhost:2181"/> <!-- 用dubbo協議在20880埠暴露服務 --> <dubbo:protocol name="dubbo" port="20880" /> <!--使用 dubbo 協議實現定義好的 api.PermissionService 介面-->
    <dubbo:service interface="service.DemoService" ref="demoService" protocol="dubbo" /> <!--具體實現該介面的 bean--> <bean id="demoService" class="Impl.DemoServiceImpl"/>

    由於ServiceBean實現了InitializingBean和ApplicationListener介面,一個是在類初始化完成後呼叫afterPropertiesSet方法,一個在Spring容器初始化完成後會呼叫onApplicationEvent方法(呼叫參考bean的載入過程),即重要的就是這兩個方法,這兩個方法

    public void afterPropertiesSet() throws Exception {
            // ServiceConfig根據dubbo:provider標籤設定provider
            // ServiceConfig根據dubbo:application標籤設定應用資訊配置
            // SerivceConfig根據dubbo:module標籤設定模組資訊配置
            // SerivceConfig設定註冊中心
            // SerivceConfig根據dubbo:monitor標籤設定監控中心配置
            // SerivceConfig設定服務提供者協議配置
         
            if (! isDelay()) {
                export();
            }
    }
    
    public void onApplicationEvent(ApplicationEvent event) {
        if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
            if (isDelay() && ! isExported() && ! isUnexported()) {
                export();
            }
        }
    }
    
    public synchronized void export() {
        //省略程式碼,從provider中獲取delay
        if (delay != null && delay > 0) {
            Thread thread = new Thread(new Runnable() {
                public void run() {
                    // 睡delay時間
                    Thread.sleep(delay);
                    // 暴露服務
                    doExport();
                }
            });
            thread.setDaemon(true);
            thread.setName("DelayExportServiceThread");
            thread.start();
        } else {
            // 沒有設定delay,直接暴露服務
            doExport();
        }
    }
    
    protected synchronized void doExport() {
        // 省略大部分校驗程式碼......
        doExportUrls();
    }
    
    
    private void doExportUrls() {
        List<URL> registryURLs = loadRegistries(true);
        // 按照不同的Protocol如(dubbo,injvm)來暴露服務
        for (ProtocolConfig protocolConfig : protocols) {
            // 1.如果協議型別為null,則預設為dubbo協議
            String name = protocolConfig.getName();
            if (name == null || name.length() == 0) {
                name = "dubbo";
            }
            // 2.獲取主機名host(省略程式碼)
            // 3.獲取埠號port(省略程式碼)
            // 4.引數設定到map中
            Map<String, String> map = new HashMap<String, String>();
            // URL中的side屬性,有兩個值,一個provider,一個consumer,暴露服務的時候為provider
            map.put(Constants.SIDE_KEY, Constants.PROVIDER_SIDE);
            // dubbo的版本號  url中的dubbo
            map.put(Constants.DUBBO_VERSION_KEY, Version.getVersion());
            // url中的timestamp
            map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
            //url中的pid
            if (ConfigUtils.getPid() > 0) {
                map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
            }
            //從其他引數中獲取引數
            appendParameters(map, application);
            appendParameters(map, module);
            appendParameters(map, provider, Constants.DEFAULT_KEY);
            appendParameters(map, protocolConfig);
            appendParameters(map, this);
            // 從暴露的方法配置資訊中獲取引數和值(程式碼省略)
            // 通用服務介面和非通用服務介面的引數
            if (generic) {
                // 通用服務介面
                map.put("generic", String.valueOf(true));
                map.put("methods", Constants.ANY_VALUE);
            } else {
                String revision = Version.getVersion(interfaceClass, version);
                if (revision != null && revision.length() > 0) {
                    map.put("revision", revision);
                }
                map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(Wrapper.getWrapper(interfaceClass).getDeclaredMethodNames())), ","));
            }
            //5.token 臨牌校驗(程式碼省略)
            //6.injvm協議處理(程式碼省略)
            //7.匯出服務
            // 獲取上下文路徑
            String contextPath = protocolConfig.getContextpath();
            if ((contextPath == null || contextPath.length() == 0) && provider != null) {
                contextPath = provider.getContextpath();
            }
            // 組裝URL
            URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
            // 如果url使用的協議存在擴充套件,呼叫對應的擴充套件來修改原url(程式碼省略)
            String scope = url.getParameter(Constants.SCOPE_KEY);
            //配置為none不暴露
            if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
                //配置不是remote的情況下做本地暴露 (配置為remote,則表示只暴露遠端服務)
                if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
                    exportLocal(url);
                }
                //如果配置不是local則暴露為遠端服務.(配置為local,則表示只暴露本地服務)
                if (! Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope) ){
                    if (registryURLs != null && registryURLs.size() > 0 && url.getParameter("register", true)) {
                        for (URL registryURL : registryURLs) {
                            // 省略部分程式碼
    	                    // 通過代理工廠將ref物件轉化成invoker物件
                            Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                            // 暴露服務(DubboProtocol來暴露服務,)
                            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);
                    }
                }
            }
            this.urls.add(url);
        }
    }
    

    注:<dubbo:reference…scope=“remote”/>,scope為remote表示引用遠端服務;為local表示引用本地服務;

    DubboProtocol中暴露服務:

    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        // 獲取URL
        URL url = invoker.getUrl();
        
        // export service.
        String key = serviceKey(url);
        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
        exporterMap.put(key, exporter);
            
        //export an stub service for dispaching event(程式碼省略)
        
        openServer(url);
        
        return exporter;
    }
    
    private void openServer(URL url) {
        // 查詢服務
        String key = url.getAddress();
        
        //client 也可以暴露一個只有server可以呼叫的服務,所以需要判斷是否是服務端
        boolean isServer = url.getParameter(Constants.IS_SERVER_KEY,true);
        if (isServer) {
            ExchangeServer server = serverMap.get(key);
            if (server == null) {
                //如果服務不存在,建立服務
                serverMap.put(key, getServer(url));
            } else {
                //server支援reset,配合override功能使用
                server.reset(url);
            }
        }
    }
    
    private ExchangeServer getServer(URL url) {
        // 忽略部分程式碼
        ExchangeServer server;
        server = Exchangers.bind(url, requestHandler);
        return server;
    }
    
    // 上述最終會呼叫到Netty
    public class NettyTransporter implements Transporter {
    
        public static final String NAME = "netty";
        
        public Server bind(URL url, ChannelHandler listener) throws RemotingException {
            return new NettyServer(url, listener);
        }
        // ......
    }