Dubbo源碼學習基礎

Dubbo源碼學習基礎

文章來源於dubbo官網和git貢獻者,結合自己理解做篩選
dubbo用戶文檔
dubbo blog

Java RMI 基本概念

// 在 RMI 調用中,有以下幾個核心的概念:

// 通過接口進行遠程調用

// 通過客戶端的 Stub 對象和服務端的 Skeleton 對象的幫助將遠程調用僞裝成本地調用

// 通過 RMI 註冊服務完成服務的註冊和發現

// 服務端的服務註冊
Hello obj = new HelloImpl(); // #1
Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0); // #2
Registry registry = LocateRegistry.createRegistry(1099); // #3
registry.rebind("Hello", stub); // #4
// 說明:

// 初始化服務對象實例,
// 通過 UnicastRemoteObject.exportObject 生成可以與服務端通訊的 Stub 對象,
// 創建一個本地的 RMI 註冊服務,監聽端口爲 1099。該註冊服務運行在服務端,也可以單獨啓動一個註冊服務的進程,
// 將 Stub 對象綁定到註冊服務上,這樣,客戶端可以通過 Hello 這個名字查找到該遠程對象。
// 客戶端的服務發現
Registry registry = LocateRegistry.getRegistry(); // #1
Hello stub = (Hello) registry.lookup("Hello"); // #2
String response = stub.sayHello(); // #3
// 說明:

// 獲取註冊服務實例,在本例中,由於沒有傳入任何參數,假定要獲取的註冊服務實例部署在本機,並監聽在 1099 端口上,
// 從註冊服務中查找服務名爲 Hello 的遠程對象,
// 通過獲取的 Stub 對象發起一次 RMI 調用並獲得結果。
// 理解 RMI 的工作原理和基本概念,對掌握現代分佈式服務框架很有幫助,建議進一步的閱讀 RMI 官方教材 [^1]。

// 瞭解 https://jameswxx.iteye.com/blog/740408

在 Dubbo 中使用註解

// 瞭解了 @EnableDubbo, @Service,@Reference 的作用,下面以一個實際的例子來展示如何使用 annotation 來開發 Dubbo 應用。以下的代碼可以在 https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-annotation 中找到。

// 3. 服務端:組裝服務提供方
// 通過 Spring 中 Java Config 的技術(@Configuration)和 annotation 掃描(@EnableDubbo)來發現、組裝、並向外提供 Dubbo 的服務。

@Configuration
@EnableDubbo(scanBasePackages = "com.alibaba.dubbo.samples.impl")
static class ProviderConfiguration {
    @Bean // #1
    public ProviderConfig providerConfig() {
        ProviderConfig providerConfig = new ProviderConfig();
        providerConfig.setTimeout(1000);
        return providerConfig;
    }

    @Bean // #2
    public ApplicationConfig applicationConfig() {
        ApplicationConfig applicationConfig = new ApplicationConfig();
        applicationConfig.setName("dubbo-annotation-provider");
        return applicationConfig;
    }

    @Bean // #3
    public RegistryConfig registryConfig() {
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setProtocol("zookeeper");
        registryConfig.setAddress("localhost");
        registryConfig.setPort(2181);
        return registryConfig;
    }

    @Bean // #4
    public ProtocolConfig protocolConfig() {
        ProtocolConfig protocolConfig = new ProtocolConfig();
        protocolConfig.setName("dubbo");
        protocolConfig.setPort(20880);
        return protocolConfig;
    }
}

// 4. 服務端:啓動服務
// 在 main 方法中通過啓動一個 Spring Context 來對外提供 Dubbo 服務。

public class ProviderBootstrap {
    public static void main(String[] args) throws Exception {
        new EmbeddedZooKeeper(2181, false).start(); // #1
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class); // #2
        context.start(); // #3
        System.in.read(); // #4
    }
}


// 6. 客戶端:組裝服務消費者
// 與 3. 服務端:組裝服務提供方 類似,通過 Spring 中 Java Config 的技術(@Configuration)和 annotation 掃描(@EnableDubbo)來發現、組裝 Dubbo 服務的消費者。

@Configuration
@EnableDubbo(scanBasePackages = "com.alibaba.dubbo.samples.action")
@ComponentScan(value = {"com.alibaba.dubbo.samples.action"})
static class ConsumerConfiguration {
    @Bean // #1
    public ApplicationConfig applicationConfig() {
        ApplicationConfig applicationConfig = new ApplicationConfig();
        applicationConfig.setName("dubbo-annotation-consumer");
        return applicationConfig;
    }

    @Bean // #2
    public ConsumerConfig consumerConfig() {
        ConsumerConfig consumerConfig = new ConsumerConfig();
        consumerConfig.setTimeout(3000);
        return consumerConfig;
    }

    @Bean // #3
    public RegistryConfig registryConfig() {
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setProtocol("zookeeper");
        registryConfig.setAddress("localhost");
        registryConfig.setPort(2181);
        return registryConfig;
    }
}

自定義容錯策略

// 如果上述內置的容錯策略無法滿足你的需求,還可以通過擴展的方式來實現自定義容錯策略。

// 擴展接口

com.alibaba.dubbo.rpc.cluster.Cluster

// 擴展配置

<dubbo:service cluster="xxx" />

// 擴展示例

// 下面通過一個例子來展示如何使用自定義的容錯策略。 Maven 項目結構:

src
 |-main
    |-java
        |-com
            |-xxx
                |-XxxCluster.java (實現Cluster接口)
    |-resources
        |-META-INF
            |-dubbo
                |-org.apache.dubbo.rpc.cluster.Cluster (純文本文件,內容爲:xxx=com.xxx.XxxClus
                
// XxxCluster.java:

package com.xxx;
 
import org.apache.dubbo.rpc.cluster.Cluster;
import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
import org.apache.dubbo.rpc.cluster.Directory;
import org.apache.dubbo.rpc.cluster.LoadBalance;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;

import java.util.List;

public class XxxCluster implements Cluster {

    @Override
    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
        return new AbstractClusterInvoker<T>() {
            @Override
            protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
                // your custimzed fault tolarence strategy goes here
            }
        };
    }

}


// META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Cluster文件的內容爲

xxx=com.xxx.XxxCluster


各種策略對比
下表對各種策略做一個簡單對比,

策略名稱	優點	缺點	主要應用場景
Failover	對調用者屏蔽調用失敗的信息	增加RT,額外資源開銷,資源浪費	對調用rt不敏感的場景
Failfast	業務快速感知失敗狀態進行自主決策	產生較多報錯的信息	非冪等性操作,需要快速感知失敗的場景
Failsafe	即使失敗了也不會影響核心流程	對於失敗的信息不敏感,需要額外的監控	旁路系統,失敗不影響核心流程正確性的場景
Failback	失敗自動異步重試	重試任務可能堆積	對於實時性要求不高,且不需要返回值的一些異步操作
Forking	並行發起多個調用,降低失敗概率	消耗額外的機器資源,需要確保操作冪等性	資源充足,且對於失敗的容忍度較低,實時性要求高的場景
Broadcast	支持對所有的服務提供者進行操作	資源消耗很大	通知所有提供者更新緩存或日誌等本地資源信息

正確加載MyFilter類

com.alibaba.dubbo.rpc.Filter接口除了要繼承自org.apache.dubbo.rpc.Filter以外,其唯一的方法invoke也需要做特殊處理。我們看看它的方法簽名:

Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;

這裏參數、返回值、異常都會被實現類MyFilter用到,因此這些類也需要有兼容類;而參數、返回值不同,對於接口來說是不同的方法,因此:

// 需要在com.alibaba.dubbo.rpc.Filter裏,定義老的invoke方法,MyFilter會覆蓋這個方法;
// org.apache.dubbo.rpc.Filter裏的invoke方法,需要找一個地方來實現橋接,框架調用Filter鏈執行到新的invoke方法時,新的參數如何轉換成老參數,老返回值如何轉換成新的返回值;

// 這裏就用到了JDK8的新特性:接口default方法。

@Deprecated
public interface Filter extends org.apache.dubbo.rpc.Filter {

    Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;

    default org.apache.dubbo.rpc.Result invoke(org.apache.dubbo.rpc.Invoker<?> invoker,
                                               org.apache.dubbo.rpc.Invocation invocation)
            throws org.apache.dubbo.rpc.RpcException {
        Result.CompatibleResult result = (Result.CompatibleResult) invoke(new Invoker.CompatibleInvoker<>(invoker),
                new Invocation.CompatibleInvocation(invocation));
        return result.getDelegate();
    }
}

// 可以看到,default方法裏,對參數進行了包裝,然後調用老的invoke方法,並將返回值進行解包後返回給Dubbo框架。這裏Result.CompatibleResult、Invocation.CompatibleInvocation以及Invoker.CompatibleInvoker都用到了代理模式。

// 感興趣的同學可以詳細看一下以下幾個類:

com.alibaba.dubbo.rpc.Invocation
com.alibaba.dubbo.rpc.Invoker
com.alibaba.dubbo.rpc.Result


Dubbo可擴展機制實戰

http://dubbo.apache.org/zh-cn/blog/introduction-to-dubbo-spi.html

Dubbo的SPI機制

Java SPI的使用很簡單。也做到了基本的加載擴展點的功能。但Java SPI有以下的不足:

  • 需要遍歷所有的實現,並實例化,然後我們在循環中才能找到我們需要的實現。
  • 配置文件中只是簡單的列出了所有的擴展實現,而沒有給他們命名。導致在程序中很難去準確的引用它們。
  • 擴展如果依賴其他的擴展,做不到自動注入和裝配
  • 不提供類似於Spring的IOC和AOP功能
    擴展很難和其他的框架集成,比如擴展裏面依賴了一個Spring bean,原生的Java SPI不支持

SPI應付一些簡單的場景是可以的,但對於Dubbo,它的功能還是比較弱的。Dubbo對原生SPI機制進行了一些擴展。接下來,我們就更深入地瞭解下Dubbo的SPI機制。

自定義一個LoadBalance擴展

本節中,我們通過一個簡單的例子,來自己實現一個LoadBalance,並把它集成到Dubbo中。我會列出一些關鍵的步驟和代碼,也可以從這個地址(https://github.com/vangoleo/dubbo-spi-demo)下載完整的demo。

到此,我們從Java SPI開始,瞭解了Dubbo SPI 的基本概念,並結合了Dubbo中的LoadBalance加深了理解。最後,我們還實踐了一下,創建了一個自定義LoadBalance,並集成到Dubbo中。相信通過這裏理論和實踐的結合,大家對Dubbo的可擴展有更深入的理解。 總結一下,Dubbo SPI有以下的特點:

  • 對Dubbo進行擴展,不需要改動Dubbo的源碼
  • 自定義的Dubbo的擴展點實現,是一個普通的Java類,Dubbo沒有引入任何Dubbo特有的元素,對代碼侵入性幾乎爲零。
  • 將擴展註冊到Dubbo中,只需要在ClassPath中添加配置文件。使用簡單。而且不會對現有代碼造成影響。符合開閉原則。
    dubbo的擴展機制設計默認值:@SPI(“dubbo”) 代表默認的spi對象
  • Dubbo的擴展機制支持IoC,AoP等高級功能
  • Dubbo的擴展機制能很好的支持第三方IoC容器,默認支持Spring - Bean,可自己擴展來支持其他容器,比如Google的Guice。
  • 切換擴展點的實現,只需要在配置文件中修改具體的實現,不需要改代碼。使用方便。

Dubbo 外部化配置(Externalized Configuration)

// 外部化配置文件
// 將以下內容的外部化配置文件物理路徑爲:classpath:/META-INF/config.properties:

# 單 Dubbo 配置 Bean 綁定
## application
dubbo.application.id = applicationBean
dubbo.application.name = dubbo-demo-application

## module
dubbo.module.id = moduleBean
dubbo.module.name = dubbo-demo-module

## registry
dubbo.registry.address = zookeeper://192.168.99.100:32770

## protocol
dubbo.protocol.name = dubbo
dubbo.protocol.port = 20880

## monitor
dubbo.monitor.address = zookeeper://127.0.0.1:32770

## provider
dubbo.provider.host = 127.0.0.1

## consumer
dubbo.consumer.client = netty

# @EnableDubboConfig 配置 Bean

/**
 * Dubbo 配置 Bean
 *
 * @author <a href="mailto:[email protected]">Mercy</a>
 */
@EnableDubboConfig
@PropertySource("META-INF/config.properties")
@Configuration
public class DubboConfiguration {

}

/**
 * Dubbo 配置引導類
 *
 * @author <a href="mailto:[email protected]">Mercy</a>
 */
public class DubboConfigurationBootstrap {

    public static void main(String[] args) {
        // 創建配置上下文
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 註冊當前配置 Bean
        context.register(DubboConfiguration.class);
        context.refresh();
 	    // application
        ApplicationConfig applicationConfig = context.getBean("applicationBean", ApplicationConfig.class);
        System.out.printf("applicationBean.name = %s \n", applicationConfig.getName());

        // module
        ModuleConfig moduleConfig = context.getBean("moduleBean", ModuleConfig.class);
        System.out.printf("moduleBean.name = %s \n", moduleConfig.getName());

        // registry
        RegistryConfig registryConfig = context.getBean(RegistryConfig.class);
        System.out.printf("registryConfig.name = %s \n", registryConfig.getAddress());

        // protocol
        ProtocolConfig protocolConfig = context.getBean(ProtocolConfig.class);
        System.out.printf("protocolConfig.name = %s \n", protocolConfig.getName());
        System.out.printf("protocolConfig.port = %s \n", protocolConfig.getPort());

        // monitor
        MonitorConfig monitorConfig = context.getBean(MonitorConfig.class);
        System.out.printf("monitorConfig.name = %s \n", monitorConfig.getAddress());

        // provider
        ProviderConfig providerConfig = context.getBean(ProviderConfig.class);
        System.out.printf("providerConfig.name = %s \n", providerConfig.getHost());

        // consumer
        ConsumerConfig consumerConfig = context.getBean(ConsumerConfig.class);
        System.out.printf("consumerConfig.name = %s \n", consumerConfig.getClient());
    }
}

// 執行結果

applicationBean.name = dubbo-demo-application 
moduleBean.name = dubbo-demo-module 
registryConfig.name = zookeeper://192.168.99.100:32770 
protocolConfig.name = dubbo 
protocolConfig.port = 20880 
monitorConfig.name = zookeeper://127.0.0.1:32770 
providerConfig.name = 127.0.0.1 
consumerConfig.name = netty

// 不難發現,@EnableDubboConfig 配置 Bean 配合外部化文件 classpath:/META-INF/config.properties,與執行輸出內容相同。

Spring應用快速集成Dubbo + Hystrix

// Hystrix 旨在通過控制那些訪問遠程系統、服務和第三方庫的節點,從而對延遲和故障提供更強大的容錯能力。Hystrix具備擁有回退機制和斷路器功能的線程和信號隔離,請求緩存和請求打包,以及監控和配置等功能。

// Dubbo是Alibaba開源的,目前國內最流行的java rpc框架。

// 本文介紹在spring應用裏,怎麼把Dubbo和Hystrix結合起來使用。

https://github.com/Netflix/Hystrix
https://github.com/apache/incubator-dubbo

// Spring Boot應用

// Demo地址:
https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-spring-boot-hystrix

生成dubbo集成spring boot的應用

//  對於不熟悉dubbo 集成spring boot應用的同學,可以在這裏直接生成dubbo + spring boot的工程: http://start.dubbo.io/

// 配置spring-cloud-starter-netflix-hystrix

// spring boot官方提供了對hystrix的集成,直接在pom.xml里加入依賴:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            <version>1.4.4.RELEASE</version>
        </dependency>

// 然後在Application類上增加@EnableHystrix來啓用hystrix starter:

@SpringBootApplication
@EnableHystrix
public class ProviderApplication {


// 配置Provider端
// 在Dubbo的Provider上增加@HystrixCommand配置,這樣子調用就會經過Hystrix代理。

@Service(version = "1.0.0")
public class HelloServiceImpl implements HelloService {
    @HystrixCommand(commandProperties = {
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") })
    @Override
    public String sayHello(String name) {
        // System.out.println("async provider received: " + name);
        // return "annotation: hello, " + name;
        throw new RuntimeException("Exception to show hystrix enabled.");
    }
}

// 配置Consumer端
// 對於Consumer端,則可以增加一層method調用,並在method上配置@HystrixCommand。當調用出錯時,會走到fallbackMethod = "reliable"的調用裏。

    @Reference(version = "1.0.0")
    private HelloService demoService;

    @HystrixCommand(fallbackMethod = "reliable")
    public String doSayHello(String name) {
        return demoService.sayHello(name);
    }
    public String reliable(String name) {
        return "hystrix fallback value";
    }
// 通過上面的配置,很簡單地就完成了Spring Boot裏Dubbo + Hystrix的集成

當Dubbo遇上Arthas:排查問題的實踐

// Arthas 是基於 Greys 進行二次開發的全新在線診斷工具,利用Java6的Instrumentation特性,動態增強你所指定的類,獲取你想要到的信息, 採用命令行交互模式,同時提供豐富的 Tab 自動補全功能,讓你在定位、分析診斷問題時看每一個操作都看起來是那麼的 

https://github.com/apache/incubator-dubbo-website/blob/asf-site/blog/zh-cn/dubbo-meet-arthas.md

如何基於Dubbo實現全異步調用鏈

https://github.com/apache/incubator-dubbo-website/blob/asf-site/blog/zh-cn/dubbo-new-async.md

// 基於Dubbo實現全異步編程,是在2.7.0版本中對現有異步方式增強後新引入的功能。本文先是回顧2.6.x及之前版本對異步的支持情況及存在的問題,引出了2.7.0版本基於CompletableFuture做了哪些針對性的增強,通過幾個示例詳細闡述了增強後的異步編程的使用方式,最後總結了引入異步模式帶來的新問題及Dubbo的解決方法。通過閱讀這篇文章,可以很容易的基於Dubbo2.7.0+版本實現一個全異步的遠程服務調用鏈路。

DUBBO協議詳解

https://github.com/apache/incubator-dubbo-website/blob/asf-site/blog/zh-cn/dubbo-protocol.md

Dubbo Rest 讓協議跑在不同的服務器上

// 目前 REST 協議在 Dubbo 中可以跑在五種不同的 server 上,分別是:

"netty": 直接基於 netty 框架的 rest server,通過 <dubbo:protocol name="rest" server="netty"/> 來配置
"tomcat": 基於嵌入式 tomcat 的 rest server,通過 <dubbo:protocol name="rest" server="tomcat"/> 來配置
"jetty": 默認選項,基於嵌入式 jetty 的 rest server,通過 <dubbo:protocol name="rest" server="jetty"/> 來配置
"sunhttp": 使用 JDK 內置的 Sun HTTP server 作爲 rest server,通過 <dubbo:protocol name="rest" server="sunhttp"/> 來配置,僅推薦在開發環境中使用
"servlet”: 採用外部應用服務器的 servlet 容器來做 rest server,這個時候,除了配置 <dubbo:protocol name="rest" server="servlet"/> 之外,還需要在 web.xml 中做額外的配置
// 由於以上的例子展示了 "netty" 作爲 rest server,下面演示一下使用嵌入式 tomcat 的 rest server 的用法。

// 注:本章節討論的示例可以通過 https://github.com/beiwei30/dubbo-rest-samples/tree/master/tomcat 來獲得

// 增加 Tomcat 相關的依賴
    <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-logging-juli</artifactId>
        </dependency>
// 配置 protocol 使用 tomcat 作爲 REST server

<dubbo:protocol name="rest" port="8080" server="tomcat"/>

// 使用外部的 Servlet 容器

// 進一步的,還可以使用外部的 servlet 容器來啓動 Dubbo 的 REST 服務。

// 注:本章節討論的示例可以通過 https://github.com/beiwei30/dubbo-rest-samples/tree/master/servlet 來獲得

// 1. 修改 pom.xml 改變打包方式
因爲使用的是外部的 servlet 容器,需要將打包方式修改爲 "war"

    <packaging>war</packaging>
// 2. 修改 rest-provider.xml
// 配置 "server" 爲 "servlet" 表示將使用外部的 servlet 容器。並配置 "contextpath" 爲 "",原因是在使用外部 servlet 容器時,Dubbo 的 REST 支持需要知道被託管的 webapp 的 contextpath 是什麼。這裏我們計劃通過 root context path 來部署應用,所以配置其爲 ""。

    <dubbo:protocol name="rest" port="8080" server="servlet" contextpath=""/>

// 3. 配置 WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <context-param> <!-- #1 -->
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/classes/spring/rest-provider.xml</param-value>
    </context-param>

    <listener>
        <listener-class>com.alibaba.dubbo.remoting.http.servlet.BootstrapListener</listener-class>
    </listener>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <servlet> <!-- #2 -->
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/api/*</url-pattern>
    </servlet-mapping>
    
// 配置 Dubbo 和 Spring 相關的 ContextListener,打開 Dubbo HTTP 支持,以及通過 rest-provider.xml 來裝配 Dubbo 服務
// 配置 Dubbo HTTP 所需的 DiapatcherServlet
// 這樣做之後,不再需要 RestProvider 來啓動 Dubbo 服務,可以將其從工程中刪掉。對應的,現在 Dubbo 的服務將會隨着 Servlet 容器的啓動而啓動。啓動完畢之後,可以通過類似 "http://localhost:8080/api/users/1" 來訪問暴露出的 REST 服務。需要注意的是,這個例子裏假定了服務提供方的 WAR 包部署在 root context path 上,所以當該應用通過 IDE 配置的 tomcat server 啓動時,需要指定 Application Context 爲 "/"。

// 增加 Swagger 支持
// 在上面使用外部 Servlet 容器的例子的基礎上,討論如何暴露 Swagger OpenApi 以及如何繼承 Swagger UI。

// 注:本章節討論的示例可以通過 https://github.com/beiwei30/dubbo-rest-samples/tree/master/servlet 來獲得

// 1. 暴露 Swagger OpenApi
// 增加 swagger 相關依賴,以便通過 "http://localhost:8080/openapi.json" 來訪問 REST 服務的描述

    <properties>
        <swagger.version>2.0.6</swagger.version>
    </properties>

	<dependencies> 
        <dependency>
            <groupId>io.swagger.core.v3</groupId>
            <artifactId>swagger-jaxrs2</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>io.swagger.core.v3</groupId>
            <artifactId>swagger-jaxrs2-servlet-initializer</artifactId>
            <version>${swagger.version}</version>
        </dependency>
    </dependencies>
// 修改 WEB-INF/web.xml,增加 openapi servlet 的配置

<web-app>
    ...
	<servlet> <!-- #3 -->
        <servlet-name>openapi</servlet-name>
        <servlet-class>io.swagger.v3.jaxrs2.integration.OpenApiServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>openapi</servlet-name>
        <url-pattern>/openapi.json</url-pattern>
        <url-pattern>/openapi.yaml</url-pattern>
    </servlet-mapping>
</web-app>
// 重新啓動應用之後,可以通過訪問 "http://localhost:8080/openapi.json" 或者 "http://localhost:8080/openapi.yaml" 來訪問暴露出的 openapi 的契約,以下是 yaml 格式的表述:

openapi: 3.0.1
paths:
  /api/users/{id}:
    get:
      operationId: getUser
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: integer
          format: int64
      responses:
        default:
          description: default response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
            text/xml:
              schema:
                $ref: '#/components/schemas/User'
  /api/users/register:
    post:
      operationId: registerUser
      requestBody:
        description: a user to register
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/User'
          text/xml:
            schema:
              $ref: '#/components/schemas/User'
      responses:
        default:
          description: default response
          content:
            application/json:
              schema:
                type: integer
                format: int64
            text/xml:
              schema:
                type: integer
                format: int64
components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
          format: int64
        name:
          type: string
// 2. 集成 Swagger UI
// 在 pom.xml 中繼續增加 swagger-ui 的依賴,這裏使用的是 webjars 的版本,從集成的角度來說更加簡潔。webjars 的工作機制可以參閱 webjars 官網 ^6

    <properties>
        <swagger.webjar.version>3.20.3</swagger.webjar.version>
    </properties>
    <dependencies> 
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>swagger-ui</artifactId>
            <version>${swagger.webjar.version}</version>
        </dependency>
    </dependencies>
// 在工程的 webapp/WEB-INF 根目錄下增加一個 HTML 文件,內容如下。HTML 文件名可以爲任何名字,沒有硬性要求,如果該文件被命名爲 "swagger-ui.html",那麼你可以通過訪問 “http://localhost:8080/swagger-ui.html" 來訪問 swagger UI。本例爲了演示方便起見,將其命名爲 "index.html",這樣當訪問 "http://localhost:8080" 時,就可以很方便的得到 swagger UI 的頁面。

// 本文沒有涉及的內容包含但不限於國際化支持、Dubbo REST 更高階的注入擴展的用法、以及 Dubbo REST 支持未來的規劃。其中 Dubbo REST 擴展的支持可以參考 https://github.com/beiwei30/dubbo-rest-samples/tree/master/extensions 中的演示

dubbo2.js解決方案

// 爲了讓對 dubbo2.js 感興趣的讀者有一個直觀的體驗,本節呈現一個快速入門示例,讓你體會到使用 dubbo2.js 調用 dubbo 服務是一件多麼輕鬆的事。

https://github.com/apache/incubator-dubbo-website/blob/asf-site/blog/zh-cn/dubbo2-js.md

https://github.com/dubbo/dubbo2.js

第一個Dubbo Filter

// 在Dubbo的整體設計中,Filter是一個很重要的概念,包括Dubbo本身的大多數功能,都是基於此擴展點實現的,在每次的調用過程中,Filter的攔截都會被執行。

// 此Filter記錄了調用過程中的狀態信息,並且通過invocation對象將客戶端設置的attachments參數傳遞到服務端。並且在調用完成後清除這些參數,這就是爲什麼請求狀態信息可以按次記錄並且進行傳遞。

https://github.com/apache/incubator-dubbo-website/blob/asf-site/blog/zh-cn/first-dubbo-filter.md

通過QoS對服務進行動態控制

// QoS目前支持的命令包括:

help: 幫助命令,列出
ls: 列出當前所有的正在提供的服務,以及消費的服務
online: 動態將某個或全部服務向註冊中心進行註冊
offline: 動態將某個或全部服務從註冊中心摘除(反註冊)
quit: 退出當前telnet會話

優化技巧:提前if判斷幫助CPU分支預測

// Dubbo裏ChannelEventRunnable的switch判斷

// 在ChannelEventRunnable裏有一個switch來判斷channel state,然後做對應的邏輯:查看

// 一個channel建立起來之後,超過99.9%情況它的state都是ChannelState.RECEIVED,那麼可以考慮把這個判斷提前

// switch對於CPU來說難以做分支預測

// 某些switch條件如果概率比較高,可以考慮單獨提前if判斷,充分利用CPU的分支預測機制

// https://github.com/apache/incubator-dubbo-website/blob/asf-site/blog/zh-cn/optimization-branch-prediction.md
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章