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:mercyblitz@gmail.com">Mercy</a>
 */
@EnableDubboConfig
@PropertySource("META-INF/config.properties")
@Configuration
public class DubboConfiguration {

}

/**
 * Dubbo 配置引导类
 *
 * @author <a href="mailto:mercyblitz@gmail.com">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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值