This is an automated email from the ASF dual-hosted git repository.
liujun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo-website.git
The following commit(s) were added to refs/heads/master by this push:
new 6c803ab269 Graalvm blog (#2734)
6c803ab269 is described below
commit 6c803ab269369e4769d6a957a8dcdee5253a9041
Author: Ken Liu <[email protected]>
AuthorDate: Wed Jun 28 21:02:33 2023 +0800
Graalvm blog (#2734)
---
assets/scss/_base.scss | 2 +-
assets/scss/_custom.scss | 1 +
.../blog/integration/dubbo-graalvm-support.md | 476 +++++++--------
.../blog/java/demos/v3.2_rest_protocol_design.md | 660 +++++++++++++++++++++
data/announcements/en/scheduled.yaml | 21 +-
data/announcements/zh/scheduled.yaml | 20 +
layouts/docs/baseof.html | 2 -
layouts/partials/toc.html | 1 +
static/imgs/blog/2023/6/graalvm/aot.png | Bin 0 -> 117731 bytes
static/imgs/blog/2023/6/graalvm/demo-1.png | Bin 0 -> 273960 bytes
static/imgs/blog/2023/6/graalvm/demo-2.png | Bin 0 -> 288228 bytes
static/imgs/blog/2023/6/graalvm/dubbo-aot-1.png | Bin 0 -> 174498 bytes
static/imgs/blog/2023/6/graalvm/dubbo-aot-2.png | Bin 0 -> 133450 bytes
.../blog/2023/6/graalvm/graalvm-advantages.png | Bin 0 -> 582360 bytes
.../blog/2023/6/graalvm/graalvm-compilation.png | Bin 0 -> 666072 bytes
.../blog/2023/6/graalvm/graalvm-copilation2.png | Bin 0 -> 1322059 bytes
.../imgs/blog/2023/6/graalvm/graalvm-principal.png | Bin 0 -> 465086 bytes
static/imgs/blog/2023/6/graalvm/language-rank.png | Bin 0 -> 650217 bytes
static/imgs/blog/2023/6/graalvm/metadata-1.png | Bin 0 -> 72102 bytes
static/imgs/blog/2023/6/graalvm/metadata-2.png | Bin 0 -> 137392 bytes
static/imgs/blog/2023/6/graalvm/metadata-3.png | Bin 0 -> 233230 bytes
.../blog/2023/6/graalvm/serverless-lang-rank.png | Bin 0 -> 954405 bytes
static/imgs/blog/2023/6/graalvm/spring-aot.png | Bin 0 -> 713069 bytes
23 files changed, 919 insertions(+), 264 deletions(-)
diff --git a/assets/scss/_base.scss b/assets/scss/_base.scss
index a17243619a..dc8a3b87e9 100755
--- a/assets/scss/_base.scss
+++ b/assets/scss/_base.scss
@@ -516,7 +516,7 @@ section#cncf {
background-size: cover;
& > .light-text {
height: 100%;
- overflow-y: scroll;
+ overflow-y: auto;
margin-left: 50vw;
word-wrap: break-word;
padding-top: 2rem;
diff --git a/assets/scss/_custom.scss b/assets/scss/_custom.scss
index e06b9d0f62..88b5c91c48 100755
--- a/assets/scss/_custom.scss
+++ b/assets/scss/_custom.scss
@@ -46,6 +46,7 @@ body {
header + .td-outer {
min-height: 50vh;
height: auto;
+ width: 100%;
}
}
diff --git a/content/zh-cn/blog/integration/dubbo-graalvm-support.md
b/content/zh-cn/blog/integration/dubbo-graalvm-support.md
index da2245c9d4..75d77b8f23 100644
--- a/content/zh-cn/blog/integration/dubbo-graalvm-support.md
+++ b/content/zh-cn/blog/integration/dubbo-graalvm-support.md
@@ -1,342 +1,298 @@
---
-title: "使用 GraalVM 构建 Native Image"
+title: "走向 Native 化:Spring&Dubbo AOT 技术示例与原理讲解"
linkTitle: "Native Image"
-date: 2021-01-14
-tags: ["Java", "Native Image"]
+date: 2023-06-28
+tags: ["Java", "Native Image", "GraalVM"]
+authors: ["刘军", "华钟明"]
description: >
- 本文讲解了如何使用 GraavlVM 构建 Dubbo 的 Native Image
+ 本文讲解了 GraavlVM 的基本工作原理,Native Image 的构建方式,如何使用 Dubbo AOT 构建打包静态化的 Dubbo
应用,解决应用“冷启动”慢、内存占用高、预热时间长等问题。
---
-# dubbo项目支持native-image
+Java 应用在云计算时代面临“冷启动”慢、内存占用高、预热时间长等问题,无法很好的适应 Serverless 等云上部署模式,GraalVM
通过静态编译、打包等技术在很大程度上解决了这些问题,同时针对 GraalVM 的一些使用限制,Spring 和 Dubbo 等主流框架也都提供了相应的 AOT
解决方案。
-## 概述
+本文我们将详细分析 Java 应用在云时代面临的挑战,GraalVM Native Image 是如何解决这些问题,GraalVM
的基本概念与工作原理,最后我们通过一个 Spring6 + Dubbo3 的微服务应用示例演示了如何将一个普通微服务应用进行静态化打包。
- 本文档将介绍将dubbo项目接入GraalVM,进行native-image编译为二进制的方式,以及我们为了达到这个目的做了哪些工作。
+本文主要分为以下四个部分展开
-
GraalVM的核心就是Graal编译器,一款优秀的JIT编译器。它即可以当作JIT编译器使用,也可以用作提前编译的静态编译器。它将完成编译的主要工作。
+1. 首先我们会先看一下在云计算快速发展的当下,云上应用应该具备的特点,Java 应用在云上所面临的挑战有哪些。
+2. 其次,我会介绍一下 GraalVM,什么是 Native Image,如何通过 GraalVM 对 Java 应用进行静态化打出 Native
Image 可执行的二进制程序。
+3. 第三部分,我们知道 GraalVM 的使用是有一定限制的,比如 Java 的反射等动态特性是不被支持的,因此我们需要提供特殊的 Metadata
配置来绕过这些限制,在这一部分我们会讲解如何加入引入 AOT Processing 来实现自动化的 Metadata 配置,包括 Spring6 框架中
AOT 处理、Dubbo3 框架的 AOT 处理等。
+4. 最后,我们将通过一个 Spring6+Dubbo3 的应用示例,来演示如何将这么一个 Java 应用进行静态化打包。
- 关于GraalVm的更多信息可以阅读
https://www.graalvm.org/docs/getting-started/container-images/ 此文档。
+## Java 应用在云时代所面临的挑战
-## 准备工作
+首先,我们先看一下云计算时代的应用特点,以及 Java 在云时代所面临的挑战。从各个统计机构给出的数据来看,Java
语言仍然是当今最受开发者欢迎的编程语言之一,仅次于一些脚本开发语言。使用 Java 语言可以非常高效的开发业务应用,丰富的生态使得 Java
具有非常高的开发和运行效率,有无数的应用基于 Java 语言开发。
- 在编译我们的dubbo项目之前,需要确保我们正基于graalVm的环境。
+
- 安装GraalVM的工作这里不做介绍,可以进入https://www.graalvm.org/
官网选取最新版本安装,安装完成后查看本地jdk可以看到如下:
+但在来到云计算时代,Java
应用的部署和运行开始面临非常多的问题。我们以Serverless为例,Serverless是云上的一种越来越主流的部署模式,它让开发者更专注业务逻辑、通过快速弹性等帮助解决资源问题,根据最新的一些数据,Java在所有云计算厂商的
Serverless 运行时中所占比例并不高,远远不能和它在传统应用开发中所占的比例相匹配。
-
+
- 这里我们使用的基于jdk1.8版本的GraalVM。
+出现这样的原因,主要是Java应用不能很好的满足Serverless场景的几个关键要求。
-## 快速上手
+- **首先是启动速度问题,Java 冷启动的耗时是比较长的**。这对于Serverless需要快速弹起的场景是一个非常大的挑战,因为 Java
应用的拉起时间可能是秒、数十秒级别的;
+-
**第二点,Java应用往往都需要一定的预热时间,才能达到最佳的性能状态**,刚刚拉起的应用如果分配比较大的流量是不合适的,往往会出现请求超时、资源占用过高等问题,这就进一步拉长了
Java 应用的有效拉起时间;
+- **第三点是 Java
应用对运行环境的要求,它往往需要较大的内存、计算资源**,而这些真正分配给业务自身的并不多,都消耗在一些JVM运行时上,这与用云降本提效的目标并补匹配;
+- **最后,Java应用打出来的包或者镜像也是非常大**,从总体上也影响存储、拉取的效率。
- 为了便于用户快速上手,我们在 Dubbo 仓库中,提供了如下demo。
+接下来,我们具体看一下针对 Java 应用所面临的这些问题, GraalVM 这样一种打包和运行时技术是如何解决的。
- 模块名dubbo-demo-native,该模块中提供了简易版本的dubbo的provider以及consumer:
+## GraalVM 简介
-
+> GraalVM compiles your Java applications ahead of time into standalone
binaries that start instantly, provide peak performance with no warmup, and use
fewer resources.
-
我们在其中使用了native-image的maven插件,并定制了一些native-image的启动参数,用户只需要对应在provider以及consumer模块下执行maven的编译打包命令:
+引用官方介绍来看,GraalVM 为 Java 应用提供 AOT 编译和二进制打包能力,基于 GraalVM
打出的二进制包可以实现快速启动、具有超高性能、无需预热时间、同时需要非常少的资源消耗。这里所说的 AOT 是发生在编译期间的一个技术简称,即
Ahead-of-time,这一点我们后续会讲到。总的来说 GraalVM 可以分为两部分内容来看
-```
-mvn -U clean package -P native -Dmaven.test.skip=true
-```
+- 首先,**GraalVM 是一个完整的 JDK 发行版本**,从这一点它是与 OpenJDK 对等的,可以运行任何面向jvm的语言开发的应用;
+- 其次,**GraalVM提供了 Native Image 打包技术**,这可以将应用打包为可以独立运行的二进制包,这个包是自包含的、可脱离 JVM
运行的应用程序。
- 编译成功可以看到如下输出:
+
-
+如上图所示,GraalVM 编译器提供了 JIT 和 AOT 两种模式。
- 在target目录中可以看到已经生成的二进制文件:
+- 对于 JIT 而言,我们都知道Java类会被编译为 .class 格式的文件,这里编译后就是 jvm 识别的字节码,在 Java 应用运行的过程中,而
JIT 编译器又将一些热点路径上的字节码编译为机器码,已实现更快的执行速度;
+- 对于 AOT 模式来说,它直接在编译期间就将字节码转换为机器码,直接省去了运行时对jvm的依赖,由于省去了 jvm 加载和字节码运行期预热的时间,AOT
编译和打包的程序具有非常高的运行时效率。
-
+
- 查看该二进制文件大小大约在40M左右:
+总的来说,JIT 使得应用可以具备更高的极限处理能力,可以降低请求的最大延迟这一关键指标;而 AOT
则可以进一步的提升应用的冷启动速度、具有更小的二进制包提及、在运行态需要更少的内存等资源。
-```
-MacdeMacBook-pro-3:target mac$ du -m demo-native-provider
-40 demo-native-provider
-```
+## 什么是 Native Image?
-(注:本例子基于zookeeper进行的服务注册和发现,所以用户需要在本地先启动一个zk,再执行二进制启动。)
+我们上面多次提到 GraalVM 中 Native Image 概念,Native Image 是一项将 Java
代码编译打包为可执行二进制程序的技术,打出的包中仅包含运行期所需要的代码,包括应用自身代码、标准依赖包、 语言运行时、JDK
库关联的静态代码。这个包的运行不再需要 jvm 环境,当然它是和具体的机器环境相绑定的,需要为不同的机器环境单独打包。 Native Image
有这里列出来的一系列特点:
- 直接运行启动该二进制:
+- 仅包含 JVM 运行所需的一部分资源,运行成本更低
+- 毫秒级的启动时间
+- 启动后即进入最佳状态,无需预热
+- 可打包为更轻量的二进制包,让部署速度更快更高效
+- 安全程度更高
-```
-MacdeMacBook-pro-3:target mac$ ./demo-native-provider
-......
-INFO: [DUBBO] DubboBootstrap is ready., dubbo version: 2.7.12-SNAPSHOT,
current host: 10.220.186.228
-Jun 15, 2021 2:29:14 PM org.apache.dubbo.common.logger.jdk.JdkLogger info
-INFO: [DUBBO] DubboBootstrap has started., dubbo version: 2.7.12-SNAPSHOT,
current host: 10.220.186.228
-Jun 15, 2021 2:29:14 PM org.apache.dubbo.common.logger.jdk.JdkLogger info
-INFO: [DUBBO] DubboBootstrap awaiting ..., dubbo version: 2.7.12-SNAPSHOT,
current host: 10.220.186.228
-```
+
- 再进入consumer的target目录,执行该二进制:
+总结起来就是这里的关键几项:更快的启动个速度、更少的资源占用、更小的安全漏洞风险、更紧凑的二进制包体积。解决 Java 应用在 Sererless
等云计算应用场景中面临的突出问题。
-```
-MacdeMacBook-pro-3:target mac$ ./demo-native-consumer
-Hello dubbo, response from provider: 10.220.186.228:20880
-```
+## GraalVM Native Image 的基本原理与使用
- 以上启动以及调用均在零点几秒内完成。
+接下来,我们看一下 GraalVM 的基本使用方式,首先,需要安装 native-image
需要的相关基础依赖,根据不同的操作系统环境会有所差异,接下来可以使用 GraalVM JDK 下载器下载
native-image。都安装好之后,第二步,就可以使用 native-image 命令编译和打包 Java 应用了,输入可以是 class
文件、jar文件、Java模块等,最终打包为一个可独立运行的可执行文件,比如这里的 HelloWorld。另外,GraalVM 也提供了对应的
Maven和Gradle构建工具插件,让打包过程更容易。
-## 我们做了哪些工作
+
-### 项目中的准备
+GraalVM 基于叫做 “closed world assumption”
即封闭世界假设的概念,要求在编译期间程序的所有运行时资源和行为即能被完全确定下来。图中是具体的 AOT
编译和打包过程,左侧应用代码、仓库、jdk等全部作为输入,GraalVM以 main
为入口,扫描所有可触达的代码与执行路径,在处理过程中可能会涉及到一些前置初始化动作,最终 AOT 编译的机器码和一些初始化资源等状态数据,被打包为可执行的
Native 包。
-#### 类初始化的辅助配置
+相比于传统的 JVM 部署模式,GraalVM Native Image 模式带来的非常大的不同。
-由于native-image是在runtime之前构建的,它的构建依赖于对哪些代码可访问的静态分析。但是,这种分析不能总是完全预测 Java 本地接口
(JNI)、Java 反射、动态代理对象或类路径资源的所有用法。需要以native-image配置文件的形式向工具提供这些动态功能未被检测到的用法。
+- GraalVM 在编译构建期间就会以 main 函数为入口,完成对应用代码的静态分析
+- 在静态分析期间无法被触达的代码,将会被移除,不会包含在最终的二进制包中
+- GraalVM 无法识别代码中的一些动态调用行为,如反射、resource资源加载、序列化、动态代理等都动态行为都将受限
+- Classpath 在构建阶段就固化下来,无法修改
+- 不再支持延迟的类加载,所有可用类和代码在程序启动阶段就确定了
+- 还有一些其他的 Java 应用能力是受限使用的(比如类初始化提前等)
-这里我们在jvm参数中加入:
+GraalVM 不支持反射等动态特性,而我们的很多应用和框架中却大量使用了反射、动态代理等特性,如何才能将这些应用打包为 Native Image
实现静态化那? GraalVM 提供了元数据配置入口,通过为所有动态特性提供配置文件,“closed world assumption”
模式还是成立的,可以让 GraalVM 在编译期知道所有的预期行为。这里给了两个例子:
-```
--agentlib:native-image-agent=config-output-dir=/path/to/config-dir/
-```
+1. 编码方式上,比如这里反射的编码方式,可以让 GraalVM 通过代码分析计算 Metadata
-以上参数将引入agent,再将项目已常规方式运行,agent将与JVM交互,拦截所有查找类、方法、字段、资源或请求代理访问的调用。然后,agent将在指定的目录中生成文件一下文件:
+
-jni-config.json,reflect-config.json,proxy-config.json以及resource-config.json......
+
-将以上文件拷入项目的
resource/META-INF/native-image文件夹下,native-image编译时对于jni、反射、动态代理以及资源相关的操作将从这些json文件中找到类的信息进行加载。
+2. 另一个示例是提供额外的 json 配置文件并放在指定的目录
META-INF/native-image/<group.id>/<artifact.id> 下。
-(注:实际上由于该agent是在运行时检测以上几类操作,而运行时势必无法走遍所有逻辑分支,所以会有些类的json信息无法生成。然而native-image编译时会对所有类进行编译,所以会导致一些预料不到的错误发生,目前只能是在发生错误时进行判断确实的类信息,手动补充到json文件中。)
+
+## AOT Processing
+Java 应用或框架中的反射等动态特性的使用是影响 GraalVM 使用的障碍,而大量的框架都存在这个限制,如果都要求应用或者开发者提供 Metadata
配置的话将会是一项非常有挑战的任务,因此,Spring 和 Dubbo 等框架都在 AOT Compilation 即 AOT 编译之前引入了 AOT
Processing 即 AOT 预处理的过程,AOT Processing 用来完成自动化的 Metadata 采集,并将 Metadata 提供给 AOT
编译器使用。
-#### native-image插件引入
+
-```
-<plugin>
- <groupId>org.graalvm.nativeimage</groupId>
- <artifactId>native-image-maven-plugin</artifactId>
- <version>21.0.0.2</version>
- <executions>
- <execution>
- <goals>
- <goal>native-image</goal>
- </goals>
- <phase>package</phase>
- </execution>
- </executions>
- <configuration>
- <skip>false</skip>
- <imageName>demo-native-provider</imageName>
- <mainClass>org.apache.dubbo.demo.graalvm.provider.Application</mainClass>
- <buildArgs>
- --no-fallback
- --initialize-at-build-time=org.slf4j.MDC
- --initialize-at-build-time=org.slf4j.LoggerFactory
- --initialize-at-build-time=org.slf4j.impl.StaticLoggerBinder
- --initialize-at-build-time=org.apache.log4j.helpers.Loader
- --initialize-at-build-time=org.apache.log4j.Logger
- --initialize-at-build-time=org.apache.log4j.helpers.LogLog
- --initialize-at-build-time=org.apache.log4j.LogManager
- --initialize-at-build-time=org.apache.log4j.spi.LoggingEvent
- --initialize-at-build-time=org.slf4j.impl.Log4jLoggerFactory
- --initialize-at-build-time=org.slf4j.impl.Log4jLoggerAdapter
- --initialize-at-run-time=io.netty.channel.epoll.Epoll
- --initialize-at-run-time=io.netty.channel.epoll.Native
- --initialize-at-run-time=io.netty.channel.epoll.EpollEventLoop
- --initialize-at-run-time=io.netty.channel.epoll.EpollEventArray
- --initialize-at-run-time=io.netty.channel.DefaultFileRegion
- --initialize-at-run-time=io.netty.channel.kqueue.KQueueEventArray
- --initialize-at-run-time=io.netty.channel.kqueue.KQueueEventLoop
- --initialize-at-run-time=io.netty.channel.kqueue.Native
- --initialize-at-run-time=io.netty.channel.unix.Errors
- --initialize-at-run-time=io.netty.channel.unix.IovArray
- --initialize-at-run-time=io.netty.channel.unix.Limits
- --initialize-at-run-time=io.netty.util.internal.logging.Log4JLogger
- --initialize-at-run-time=io.netty.channel.unix.Socket
- --initialize-at-run-time=io.netty.channel.ChannelHandlerMask
- --report-unsupported-elements-at-runtime
- --allow-incomplete-classpath
- --enable-url-protocols=http
- -H:+ReportExceptionStackTraces
- </buildArgs>
- </configuration>
-</plugin>
-```
+AOT 编译机制是对所有 Java 应用通用的,但相比于 AOT 编译,AOT Processing 采集 Metadata
的过程是每个框架都不同的,因为每个框架对于反射、动态代理等都有自己的用法。
+我们以一个典型的 Spring + Dubbo 的微服务应用为例,要实现这个应用的静态化打包,这里涉及到 Spring、Dubbo 以及一众第三方依赖的
Metadata 处理过程。
- 引入该插件后执行maven的打包等命令时就能自动执行native-image的命令。
+- Spring - Spring AOT processing
+- Dubbo - Dubbo AOT processing
+- Third-party libraries - Reachability Metadata
-#### 引入生成的SPI代码依赖包
+对于 Spring 来说,Spring6 中发布了 Spring AOT 机制,用来支持 Spring 应用的静态化预处理;Dubbo 最近也在 3.2
版本中发布了 Dubbo AOT 机制,让 Dubbo 相关组件可以自动化实现 Native
预处理;除了这两个与业务开发密切相关的框架,一个应用中往往还有大量的第三方依赖,这些依赖的 Metadata
也是影响静态化的关键,如果它们中有反射、类加载等行为,那么需要为它们提供 Metadata 配置,对于这些第三方应用目前有两个渠道,一个是 GraalVM
官方提供的共享空间,这里有相当一部分依赖的 Metadata
配置可供使用(https://github.com/oracle/graalvm-reachability-metadata),另一种方式则是要求组件官方发布的发布中包含
Metadata 配置,对于这两种情况 GraalVM 都可以做到对于 Metadata 的自动读取。
-
由于dubbo项目基于SPI的模式开放了极高的可扩展性,而该模式依赖了一些扩展适配的生成代码,在常规模式下运行的项目是没有问题的,但编译为二进制的映像无法支持动态生成编译代码,这里我们是这么解决的:
+### Spring AOT
-- 将生成的源码导出:
+接下来我们看一下 Spring AOT 做了哪些编译之前的预处理工作,Spring 框架中有非常多的动态特性,比如自动配置、条件 Bean
等特性。Spring AOT 就是针对针对这些动态特性,在构建阶段进行预处理,生成可供 GraalVM 使用的一系列 Metadata
输入,这里生成的内容包括:
-(注:这里我们一定程度上改动了dubbo-common中compiler的代码,后续会细说)
+- Spring Bean 定义相关的代码预生成,如下图展示代码段
+- 在构建阶段生成动态代理相关代码
+- 关于一些反射等使用的 JSON 元数据文件
-```
-public Class<?> doCompile(String name, String source) throws Throwable {
- System.*out*.println("--->write code:" + name);
- Files.*write*(Paths.*get*("/tmp/sources/" + name), source.getBytes());
- ......
-}
-```
+
-- 将导出的源码拷入dubbo-graalvm模块中:
+### Dubbo AOT
+Dubbo AOT 做的事情与 Spring AOT 非常类似,只不过 Dubbo AOT 是专门针对 Dubbo 框架特有的使用方式进行预处理,这包括:
-
+- SPI 扩展相关的源代码生成
+- 一些反射使用的 JSON 配置文件生成
+- RPC 代理类代码生成
-- 在新项目中引入该模块:
+
- pom依赖如下:
+
-```
-<dependency>
- <groupId>org.apache.dubbo</groupId>
- <artifactId>dubbo-graalvm</artifactId>
- <version>2.7.12-SNAPSHOT</version>
-</dependency>
-```
+## Spring6 + Dubbo3 示例演示
-- (注:基于本分支版本的dubbo可以直接依赖该包,后续其他版本若有新增SPI的话需要再导出一遍源码进行更新)
+接下来,我们通过一个 Spring6 + Dubbo3 的示例微服务应用,演示如何使用 Spring AOT、Dubbo AOT 等,来实现应用的
Native Image 打包。
- 引入该依赖后,在doCompiler的过程中我们便能够通过类名直接找到该class,无需再动态生成:
+完整的代码示例可在这里下载:[dubbo-samples-native-image](https://github.com/apache/dubbo-samples/tree/master/1-basic/dubbo-samples-native-image)
-```
-@Override
-public Class<?> doCompile(String name, String sourceCode) throws Throwable {
- try {
- Class<?> res = Class.*forName*(name);
- return res;
- } catch (Throwable ex) {
- //ignore
- }
- //......
-}
-```
+### 第一步:安装GraalVM
- 到这里项目关于native-image编译所需的依赖就配置完成了,直接打包即可生成二进制文件。
+1.
在Graalvm官网根据自己的系统选取对应Graalvm版本:[https://www.graalvm.org/downloads/](https://www.graalvm.org/downloads/)
+2. 根据官方文档安装 native-image:[Getting Started with Native
Image](https://www.graalvm.org/latest/reference-manual/native-image/#install-native-image)
-## 对dubbo源码的改动
+### 第二步:创建项目
- 本分支名为:native_dubbo_0611,基于tag
[dubbo-2.7.12](https://github.com/apache/dubbo/releases/tag/dubbo-2.7.12)版本进行修改。这里对我们进行的主要的改动做一些说明。
+这个示例应用就是普通的、常见的微服务应用,我们使用 SpringBoot3 进行应用配置开发,使用 Dubbo3 定义并发布 RPC 服务;应用构建工具使用
Maven。
-- 规避动态生成代码
+
-```
-//dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/JavassistCompiler.java
-@Override
-public Class<?> doCompile(String name, String source) throws Throwable {
- try {
- Class<?> res = Class.*forName*(name);
- return res;
- } catch (Throwable ex) {
- //ignore
- }
- CtClassBuilder builder = new CtClassBuilder();
- builder.setClassName(name)
- //......
-}
-//dubbo-common/src/main/java/org/apache/dubbo/common/compiler/support/JdkCompiler.java
-@Override
-public Class<?> doCompile(String name, String sourceCode) throws Throwable {
- try {
- Class<?> res = Class.*forName*(name);
- return res;
- } catch (Throwable ex) {
- //ignore
- }
- int i = name.lastIndexOf('.');
- String packageName = i < 0 ? "" : name.substring(0, i);
- //......
-}
-```
+
-
这里针对dubbo中的两类代码编译方式都做了修改,需要编译源码时先根据全类名反射查找本地是否已经加载了该类,实际上也就是我们引入的dubbo-graalvm中包含的生成代码,引入该包后这里将直接返回加载好的Class,无需动态进行编译。
+### 第三步:配置 Maven 插件
-- 替换ExtensionLoader中的代码生成
+重点是增加 spring-boot-maven-plugin、native-maven-plugin、dubbo-maven-plugin
三个插件配置,开启 AOT
处理过程,修改dubbo-maven-plugin中的mainClass为所需的启动类全路径。(其中API使用方式无需添加spring-boot-maven-plugin依赖。)
-```
-//dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java
-private Class<?> createAdaptiveExtensionClass() {
- try {
- Class c = Class.*forName*(generatePackageInfo() + "." +
type.getSimpleName() + "$Adaptive");
- return c;
- } catch (Throwable e) {
- //ignore
- }
- String code = new
AdaptiveClassCodeGenerator(type,cachedDefaultName).generate();
- //......
-}
-private static final String *CODE_PACKAGE* = "%s";
-private String generatePackageInfo() {
- return String.*format*(*CODE_PACKAGE*, type.getPackage().getName());
-}
+```xml
+ <profiles>
+ <profile>
+ <id>native</id>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <release>17</release>
+ <fork>true</fork>
+ <verbose>true</verbose>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>process-aot</id>
+ <goals>
+ <goal>process-aot</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.graalvm.buildtools</groupId>
+ <artifactId>native-maven-plugin</artifactId>
+ <version>0.9.20</version>
+ <configuration>
+
<classesDirectory>${project.build.outputDirectory}</classesDirectory>
+ <metadataRepository>
+ <enabled>true</enabled>
+ </metadataRepository>
+ <requiredVersion>22.3</requiredVersion>
+ </configuration>
+ <executions>
+ <execution>
+ <id>add-reachability-metadata</id>
+ <goals>
+ <goal>add-reachability-metadata</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-maven-plugin</artifactId>
+ <version>${dubbo.version}</version>
+ <configuration>
+
<mainClass>com.example.nativedemo.NativeDemoApplication</mainClass>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>process-sources</phase>
+ <goals>
+ <goal>dubbo-process-aot</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
```
- ExtensionLoader是整个扩展机制的主要逻辑类,在这个类里面实现了配置的加载、扩展类缓存、自适应对象生成等所有工作。
+### 第四步:在Pom依赖中添加native相关的依赖
-
在getAdaptiveExtension()方法中,会为扩展点接口自动生成实现类字符串。而这里同第一个变更一样,由于我们直接引入了生成好的SPI代码包,这里边不再需要进行代码生成,直接根据类名获取本地的Class即可,这里注意生成的扩展类名都以
$Adaptive 为后缀,查找类时需要拼接。找到实现类直接返回。
-
-- Reference/ServiceConfig中替换Wrapper的用法
-
-```
-//dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
-// String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
- String[] methods =
Arrays.*stream*(interfaceClass.getMethods()).map(it->it.getName()).toArray(String[]::new);
- if (methods.length == 0) {
- l*ogger*.warn("No method found in service interface " +
interfaceClass.getName());
- map.put(*METHODS_KEY*, *ANY_VALUE*);
- //......
-}
-//dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
-// String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
- String[] methods =
Arrays.*stream*(interfaceClass.getMethods()).map(it->it.getName()).toArray(String[]::new);
- if (methods.length == 0) {
- l*ogger*.warn("No method found in service interface " +
interfaceClass.getName());
- map.put(*METHODS_KEY*, *ANY_VALUE*);
- //......
-}
+另外,对于 Dubbo 而言,由于当前一些 Native 机制依赖 JDK17 等版本,Dubbo
没有将一些包默认打包到发行版本中,因此需要增加两个额外的依赖 dubbo-spring6 适配和 dubbo-native 组件。
+```xml
+<dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-config-spring6</artifactId>
+ <version>${dubbo.version}</version>
+</dependency>
+<dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-native</artifactId>
+ <version>${dubbo.version}</version>
+</dependency>
```
- 这里由于Wrapper的getWrapper需要进行动态的代码生成,所以直接换成反射的getMethods。
+### 第五步:调整compiler、proxy、serialization和logger
+
+同时,这个示例对于第三方组件的支持目前也是受限的,主要是第三方组件的 Reachability Metadata 。比如目前支持的网络通信或编码组件有
Netty 和 Fastjson2;支持的日志等组件为 Logback;微服务组件有 Nacos、Zookeeper 等。
+
+- 序列化方式目前支持的比较好的是Fastjson2
+- compiler、proxy目前只能选择jdk
+- logger目前需要配置slf4j,目前仅支持logback
+
+示例配置如下:
+```yaml
+dubbo:
+ application:
+ name: ${spring.application.name}
+ logger: slf4j
+ protocol:
+ name: dubbo
+ port: -1
+ serialization: fastjson2
+ registry:
+ id: zk-registry
+ address: zookeeper://127.0.0.1:2181
+ config-center:
+ address: zookeeper://127.0.0.1:2181
+ metadata-report:
+ address: zookeeper://127.0.0.1:2181
+ provider:
+ serialization: fastjson2
+ consumer:
+ serialization: fastjson2
+```
-- 加入dubbo-graalvm模块
+### 第六步:编译
- 该模块用处上面已经说明。
+在项目根路径下执行以下编译命令:
-- 替换fastjson为gson
+- API方式直接执行
```
-//dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
-@Override
-public void publishServiceDefinition(URL providerUrl) {
- try {
- if (!ProtocolUtils.*isGeneric*(providerUrl.getParameter(*GENERIC_KEY*))) {
- String interfaceName = providerUrl.getParameter(*INTERFACE_KEY*);
- if (StringUtils.*isNotEmpty*(interfaceName)) {
- Class interfaceClass = Class.*forName*(interfaceName);
- ServiceDefinition serviceDefinition =
ServiceDefinitionBuilder.*build*(interfaceClass);
- String data = new Gson().toJson(serviceDefinition);
- serviceDefinitions.put(providerUrl.getServiceKey(), data);
- return;
- }
- //......
-}
+ mvn clean install -P native -Dmaven.test.skip=true
```
- 由于fastjson中使用要了cglib进行动态代理,而native-image编译目前还不支持cglib,所以这里换成gson。
+- 注解和xml方式(Springboot3集成的方式)
-- 多接口proxy调用替换set为list
-
-```
-//dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyFactory.java
-@Override
-public <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException
{
- List<Class<?>> interfaces = new ArrayList<>();
- String config = invoker.getUrl().getParameter(*INTERFACES*);
- if (config != null && config.length() > 0) {
- String[] types = *COMMA_SPLIT_PATTERN*.split(config);
- for (String type : types) {
- // *TODO can we load successfully for a different classloader?.*
- interfaces.add(ReflectUtils.*forName*(type));
- }
- }
- //......
-}
+```shell
+ mvn clean install -P native native:compile -Dmaven.test.skip=true
```
- 这里由于该service拥有多个接口获取proxy时需要是有序的,原先使用的set不满足需求,这里用ArrayList替代。
+### 第七步:执行二进制文件即可
+
+二进制文件在 target/ 目录下,一般以工程名称为二进制包的名称,比如 target/native-demo
+
+## 总结
- 以上就是为了支持graalvm对dubbo源码的主要改动点。
+GraalVM 技术为 Java 在云计算时代的应用带来了新的变革,帮助解决了 Java 应用启动慢、资源占用,但同时我们也看到了 GraalVM
的使用也存在一些限制,因此 Spring6、SpringBoot3、Dubbo3 都提供了相应的 Native 解决方案。 Apache Dubbo
社区接下来将在周边生态组件等推进整体的 Native 静态化。
diff --git a/content/zh-cn/blog/java/demos/v3.2_rest_protocol_design.md
b/content/zh-cn/blog/java/demos/v3.2_rest_protocol_design.md
new file mode 100644
index 0000000000..d537f23648
--- /dev/null
+++ b/content/zh-cn/blog/java/demos/v3.2_rest_protocol_design.md
@@ -0,0 +1,660 @@
+---
+aliases:
+ -
/zh/docs3-v2/java-sdk/concepts-and-architecture/v3.2_rest_protocol_design/
+ -
/zh-cn/docs3-v2/java-sdk/concepts-and-architecture/v3.2_rest_protocol_design/
+ -
/zh-cn/overview/mannual/java-sdk/concepts-and-architecture/v3.2_rest_protocol_design/
+ -
/zh-cn/overview/mannual/java-sdk/reference-manual/protocol/v3.2_rest_protocol_design/
+description: 本文将介绍 Dubbo 2.x 版本中的 Rest 协议及其未来的重构方向。
+linkTitle: 2.7 版本 Rest 协议实现重构设想
+title: Rest 协议
+tags: ["Java"]
+date: 2022-07-26
+---
+
+
+
+
+# Dubbo RestProtocol 设计文档
+
+## 原版本dubbo rest
+
+consumer
+
+restClient支持 依赖resteasy 不支持spring mvc
+
+provider(较重)
+
+依赖web container (tomcat,jetty,)servlet 模式,jaxrs netty server
+
+### 改版dubbo rest
+
+方向:
+
+更加轻量,具有dubbo风格的rest,微服务体系互通(Springcloud Alibaba)
+
+1.注解解析
+
+2.报文编解码
+
+3.restClient
+
+4.restServer(netty)
+
+支持程度:
+
+content-type text json xml form(后续会扩展)
+
+注解
+
+param,header,body,pathvariable (spring mvc & resteasy)
+
+## Http 协议报文
+
+ POST /test/path? HTTP/1.1
+ Host: localhost:8080
+ Connection: keep-alive
+ Content-type: application/json
+
+
+ {"name":"dubbo","age":10,"address":"hangzhou"}
+
+
+
+### dubbo http(header)
+
+ // service key header
+ path: com.demo.TestInterface
+ group: demo
+ port: 80
+ version: 1.0.0
+
+ // 保证长连接
+ Keep-Alive,Connection: keep-alive
+ Keep-alive: 60
+
+ // RPCContext Attachment
+ userId: 123456
+
+
+## 目前支持粒度:
+
+| 数据位置 | content-type | spring注解 | resteasy注解 |
+| --- | --- | --- | --- |
+| body | 无要求 | ReuqestBody | 无注解即为body |
+| querystring(?test=demo) | 无要求 | RequestParam | QueryParam |
+| header | 无要求 | RequestHeader | PathParam |
+| form | application/x-www-form-urlencoded | RequestParam ReuqestBody |
FormParam |
+| path | 无要求 | PathVariable | PathParam |
+| method | 无要求 | PostMapping GetMapping | GET POST |
+| url | | PostMapping GetMapping path属性 | Path |
+| content-type | | PostMapping GetMapping consumers属性 | Consumers |
+| Accept | | PostMapping GetMapping produces属性 | Produces |
+
+## rest注解解析(ServiceRestMetadataResolver)
+
+ JAXRSServiceRestMetadataResolver
+
+ SpringMvcServiceRestMetadataResolver
+
+ServiceRestMetadata
+
+ public class ServiceRestMetadata implements Serializable {
+
+ private String serviceInterface; // com.demo.TestInterface
+
+ private String version;// 1.0.0
+
+ private String group;// demo
+
+ private Set<RestMethodMetadata> meta;// method 元信息
+
+ private int port;// 端口 for provider service key
+
+ private boolean consumer;// consumer 标志
+
+ /**
+ * make a distinction between mvc & resteasy
+ */
+ private Class codeStyle;//
+
+ /**
+ * for provider
+ */
+ private Map<PathMatcher, RestMethodMetadata> pathToServiceMap;
+
+ /**
+ * for consumer
+ */
+ private Map<String, Map<ParameterTypesComparator, RestMethodMetadata>>
methodToServiceMa
+
+RestMethodMetadata
+
+ public class RestMethodMetadata implements Serializable {
+
+ private MethodDefinition method; // method 定义信息(name
,pramType,returnType)
+
+ private RequestMetadata request;// 请求元信息
+
+ private Integer urlIndex;
+
+ private Integer bodyIndex;
+
+ private Integer headerMapIndex;
+
+ private String bodyType;
+
+ private Map<Integer, Collection<String>> indexToName;
+
+ private List<String> formParams;
+
+ private Map<Integer, Boolean> indexToEncoded;
+
+ private ServiceRestMetadata serviceRestMetadata;
+
+ private List<ArgInfo> argInfos;
+
+ private Method reflectMethod;
+
+ /**
+ * make a distinction between mvc & resteasy
+ */
+ private Class codeStyle;
+
+
+ArgInfo
+
+ public class ArgInfo {
+ /**
+ * method arg index 0,1,2,3
+ */
+ private int index;
+ /**
+ * method annotation name or name
+ */
+ private String annotationNameAttribute;
+
+ /**
+ * param annotation type
+ */
+ private Class paramAnnotationType;
+
+ /**
+ * param Type
+ */
+ private Class paramType;
+
+ /**
+ * param name
+ */
+ private String paramName;
+
+ /**
+ * url split("/") String[n] index
+ */
+ private int urlSplitIndex;
+
+ private Object defaultValue;
+
+ private boolean formContentType;
+
+RequestMeatadata
+
+ public class RequestMetadata implements Serializable {
+
+ private static final long serialVersionUID = -240099840085329958L;
+
+ private String method;// 请求method
+
+ private String path;// 请求url
+
+
+ private Map<String, List<String>> params // param参数?拼接
+
+ private Map<String, List<String>> headers// header;
+
+ private Set<String> consumes // content-type;
+
+ private Set<String> produces // Accept;
+
+### Consumer 代码:
+
+refer:
+
+ @Override
+ protected <T> Invoker<T> protocolBindingRefer(final Class<T> type,
final URL url) throws RpcException {
+
+ // restClient spi创建
+ ReferenceCountedClient<? extends RestClient> refClient =
+ clients.computeIfAbsent(url.getAddress(), key ->
createReferenceCountedClient(url, clients));
+
+ refClient.retain();
+
+ // resolve metadata
+ Map<String, Map<ParameterTypesComparator, RestMethodMetadata>>
metadataMap = MetadataResolver.resolveConsumerServiceMetadata(type, url);
+
+ ReferenceCountedClient<? extends RestClient> finalRefClient =
refClient;
+ Invoker<T> invoker = new AbstractInvoker<T>(type, url, new
String[]{INTERFACE_KEY, GROUP_KEY, TOKEN_KEY}) {
+ @Override
+ protected Result doInvoke(Invocation invocation) {
+ try {
+ // 获取 method的元信息
+ RestMethodMetadata restMethodMetadata =
metadataMap.get(invocation.getMethodName()).get(ParameterTypesComparator.getInstance(invocation.getParameterTypes()));
+
+ RequestTemplate requestTemplate = new
RequestTemplate(invocation, restMethodMetadata.getRequest().getMethod(),
url.getAddress(), getContextPath(url));
+
+ HttpConnectionCreateContext
httpConnectionCreateContext = new HttpConnectionCreateContext();
+ // TODO dynamic load config
+ httpConnectionCreateContext.setConnectionConfig(new
HttpConnectionConfig());
+
httpConnectionCreateContext.setRequestTemplate(requestTemplate);
+
httpConnectionCreateContext.setRestMethodMetadata(restMethodMetadata);
+ httpConnectionCreateContext.setInvocation(invocation);
+ httpConnectionCreateContext.setUrl(url);
+
+
// http 信息构建拦截器
+ for (HttpConnectionPreBuildIntercept intercept :
httpConnectionPreBuildIntercepts) {
+ intercept.intercept(httpConnectionCreateContext);
+ }
+
+
+ CompletableFuture<RestResult> future =
finalRefClient.getClient().send(requestTemplate);
+ CompletableFuture<AppResponse> responseFuture = new
CompletableFuture<>();
+ AsyncRpcResult asyncRpcResult = new
AsyncRpcResult(responseFuture, invocation);
+ // response 处理
+ future.whenComplete((r, t) -> {
+ if (t != null) {
+ responseFuture.completeExceptionally(t);
+ } else {
+ AppResponse appResponse = new AppResponse();
+ try {
+ int responseCode = r.getResponseCode();
+ MediaType mediaType = MediaType.TEXT_PLAIN;
+
+ if (400 < responseCode && responseCode <
500) {
+ throw new
HttpClientException(r.getMessage());
+ } else if (responseCode >= 500) {
+ throw new
RemoteServerInternalException(r.getMessage());
+ } else if (responseCode < 400) {
+ mediaType =
MediaTypeUtil.convertMediaType(r.getContentType());
+ }
+
+
+ Object value =
HttpMessageCodecManager.httpMessageDecode(r.getBody(),
+
restMethodMetadata.getReflectMethod().getReturnType(), mediaType);
+ appResponse.setValue(value);
+ Map<String, String> headers = r.headers()
+ .entrySet()
+ .stream()
+
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0)));
+ appResponse.setAttachments(headers);
+ responseFuture.complete(appResponse);
+ } catch (Exception e) {
+ responseFuture.completeExceptionally(e);
+ }
+ }
+ });
+ return asyncRpcResult;
+ } catch (RpcException e) {
+ if (e.getCode() == RpcException.UNKNOWN_EXCEPTION) {
+ e.setCode(getErrorCode(e.getCause()));
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ invokers.remove(this);
+ destroyInternal(url);
+ }
+ };
+ invokers.add(invoker);
+ return invoker;
+
+### provider 代码:
+
+export:
+
+ public <T> Exporter<T> export(final Invoker<T> invoker) throws
RpcException {
+ URL url = invoker.getUrl();
+ final String uri = serviceKey(url);
+ Exporter<T> exporter = (Exporter<T>) exporterMap.get(uri);
+ if (exporter != null) {
+ // When modifying the configuration through override, you need
to re-expose the newly modified service.
+ if (Objects.equals(exporter.getInvoker().getUrl(),
invoker.getUrl())) {
+ return exporter;
+ }
+ }
+
+
+ // TODO addAll metadataMap to RPCInvocationBuilder metadataMap
+ Map<PathMatcher, RestMethodMetadata> metadataMap =
MetadataResolver.resolveProviderServiceMetadata(url.getServiceModel().getProxyObject().getClass(),url);
+
+ PathAndInvokerMapper.addPathAndInvoker(metadataMap, invoker);
+
+
+ final Runnable runnable = doExport(proxyFactory.getProxy(invoker,
true), invoker.getInterface(), invoker.getUrl());
+ exporter = new AbstractExporter<T>(invoker) {
+ @Override
+ public void afterUnExport() {
+ exporterMap.remove(uri);
+ if (runnable != null) {
+ try {
+ runnable.run();
+ } catch (Throwable t) {
+ logger.warn(PROTOCOL_UNSUPPORTED, "", "",
t.getMessage(), t);
+ }
+ }
+ }
+ };
+ exporterMap.put(uri, exporter);
+ return exporter;
+ }
+
+RestHandler
+
+ private class RestHandler implements HttpHandler<HttpServletRequest,
HttpServletResponse> {
+
+ @Override
+ public void handle(HttpServletRequest servletRequest,
HttpServletResponse servletResponse) throws IOException, ServletException {
+ // 有servlet reuqest 和nettyRequest
+ RequestFacade request =
RequestFacadeFactory.createRequestFacade(servletRequest);
+
RpcContext.getServiceContext().setRemoteAddress(request.getRemoteAddr(),
request.getRemotePort());
+ // dispatcher.service(request, servletResponse);
+
+ Pair<RpcInvocation, Invoker> build = null;
+ try {
+ // 根据请求信息创建 RPCInvocation
+ build = RPCInvocationBuilder.build(request,
servletRequest, servletResponse);
+ } catch (PathNoFoundException e) {
+ servletResponse.setStatus(404);
+ }
+
+ Invoker invoker = build.getSecond();
+
+ Result invoke = invoker.invoke(build.getFirst());
+
+ // TODO handling exceptions
+ if (invoke.hasException()) {
+ servletResponse.setStatus(500);
+ } else {
+
+ try {
+ Object value = invoke.getValue();
+ String accept = request.getHeader(RestConstant.ACCEPT);
+ MediaType mediaType =
MediaTypeUtil.convertMediaType(accept);
+ // TODO write response
+
HttpMessageCodecManager.httpMessageEncode(servletResponse.getOutputStream(),
value, invoker.getUrl(), mediaType);
+ servletResponse.setStatus(200);
+ } catch (Exception e) {
+ servletResponse.setStatus(500);
+ }
+
+
+ }
+
+ // TODO add Attachment header
+
+
+ }
+ }
+
+RPCInvocationBuilder
+
+ {
+
+
+ private static final ParamParserManager paramParser = new
ParamParserManager();
+
+
+ public static Pair<RpcInvocation, Invoker> build(RequestFacade
request, Object servletRequest, Object servletResponse) {
+
+ // 获取invoker
+ Pair<Invoker, RestMethodMetadata> invokerRestMethodMetadataPair =
getRestMethodMetadata(request);
+
+ RpcInvocation rpcInvocation = createBaseRpcInvocation(request,
invokerRestMethodMetadataPair.getSecond());
+
+ ProviderParseContext parseContext = createParseContext(request,
servletRequest, servletResponse, invokerRestMethodMetadataPair.getSecond());
+ // 参数构建
+ Object[] args = paramParser.providerParamParse(parseContext);
+
+ rpcInvocation.setArguments(args);
+
+ return Pair.make(rpcInvocation,
invokerRestMethodMetadataPair.getFirst());
+
+ }
+
+ private static ProviderParseContext createParseContext(RequestFacade
request, Object servletRequest, Object servletResponse, RestMethodMetadata
restMethodMetadata) {
+ ProviderParseContext parseContext = new
ProviderParseContext(request);
+ parseContext.setResponse(servletResponse);
+ parseContext.setRequest(servletRequest);
+
+ Object[] objects = new
Object[restMethodMetadata.getArgInfos().size()];
+ parseContext.setArgs(Arrays.asList(objects));
+ parseContext.setArgInfos(restMethodMetadata.getArgInfos());
+
+
+ return parseContext;
+ }
+
+ private static RpcInvocation createBaseRpcInvocation(RequestFacade
request, RestMethodMetadata restMethodMetadata) {
+ RpcInvocation rpcInvocation = new RpcInvocation();
+
+
+ int localPort = request.getLocalPort();
+ String localAddr = request.getLocalAddr();
+ int remotePort = request.getRemotePort();
+ String remoteAddr = request.getRemoteAddr();
+
+ String HOST = request.getHeader(RestConstant.HOST);
+ String GROUP = request.getHeader(RestConstant.GROUP);
+
+ String PATH = request.getHeader(RestConstant.PATH);
+ String VERSION = request.getHeader(RestConstant.VERSION);
+
+ String METHOD = restMethodMetadata.getMethod().getName();
+ String[] PARAMETER_TYPES_DESC =
restMethodMetadata.getMethod().getParameterTypes();
+
+
rpcInvocation.setParameterTypes(restMethodMetadata.getReflectMethod().getParameterTypes());
+
+
+ rpcInvocation.setMethodName(METHOD);
+ rpcInvocation.setAttachment(RestConstant.GROUP, GROUP);
+ rpcInvocation.setAttachment(RestConstant.METHOD, METHOD);
+ rpcInvocation.setAttachment(RestConstant.PARAMETER_TYPES_DESC,
PARAMETER_TYPES_DESC);
+ rpcInvocation.setAttachment(RestConstant.PATH, PATH);
+ rpcInvocation.setAttachment(RestConstant.VERSION, VERSION);
+ rpcInvocation.setAttachment(RestConstant.HOST, HOST);
+ rpcInvocation.setAttachment(RestConstant.REMOTE_ADDR, remoteAddr);
+ rpcInvocation.setAttachment(RestConstant.LOCAL_ADDR, localAddr);
+ rpcInvocation.setAttachment(RestConstant.REMOTE_PORT, remotePort);
+ rpcInvocation.setAttachment(RestConstant.LOCAL_PORT, localPort);
+
+ Enumeration<String> attachments =
request.getHeaders(RestConstant.DUBBO_ATTACHMENT_HEADER);
+
+ while (attachments != null && attachments.hasMoreElements()) {
+ String s = attachments.nextElement();
+
+ String[] split = s.split("=");
+
+ rpcInvocation.setAttachment(split[0], split[1]);
+ }
+
+
+ // TODO set path,version,group and so on
+ return rpcInvocation;
+ }
+
+
+ private static Pair<Invoker, RestMethodMetadata>
getRestMethodMetadata(RequestFacade request) {
+ String path = request.getRequestURI();
+ String version = request.getHeader(RestConstant.VERSION);
+ String group = request.getHeader(RestConstant.GROUP);
+ int port = request.getIntHeader(RestConstant.REST_PORT);
+
+ return PathAndInvokerMapper.getRestMethodMetadata(path, version,
group, port);
+ }
+
+
+ }
+
+## 编码示例
+
+API
+
+mvc:
+
+ @RestController()
+ @RequestMapping("/demoService")
+ public interface DemoService {
+ @RequestMapping(value = "/hello", method = RequestMethod.GET)
+ Integer hello(@RequestParam Integer a, @RequestParam Integer b);
+
+ @RequestMapping(value = "/error", method = RequestMethod.GET)
+ String error();
+
+ @RequestMapping(value = "/say", method = RequestMethod.POST, consumes
= MediaType.TEXT_PLAIN_VALUE)
+ String sayHello(@RequestBody String name);
+ }
+
+resteasy:
+
+ @Path("/demoService")
+ public interface RestDemoService {
+ @GET
+ @Path("/hello")
+ Integer hello(@QueryParam("a")Integer a,@QueryParam("b") Integer b);
+
+ @GET
+ @Path("/error")
+ String error();
+
+ @POST
+ @Path("/say")
+ @Consumes({MediaType.TEXT_PLAIN})
+ String sayHello(String name);
+
+ boolean isCalled();
+ }
+
+impl(service)
+
+ @DubboService()
+ public class RestDemoServiceImpl implements RestDemoService {
+ private static Map<String, Object> context;
+ private boolean called;
+
+
+ @Override
+ public String sayHello(String name) {
+ called = true;
+ return "Hello, " + name;
+ }
+
+
+ public boolean isCalled() {
+ return called;
+ }
+
+ @Override
+ public Integer hello(Integer a, Integer b) {
+ context = RpcContext.getServerAttachment().getObjectAttachments();
+ return a + b;
+ }
+
+
+ @Override
+ public String error() {
+ throw new RuntimeException();
+ }
+
+ public static Map<String, Object> getAttachments() {
+ return context;
+ }
+ }
+
+## 流程图
+
+**Consumer**
+
+
+
+**Provider(RestServer)**
+
+
+
+## 场景 :
+
+**非dubbo体系互通(Springcloud alibaba 互通)**
+
+互通条件:
+
+| | 协议 | Dubbo | SpringCloud Alibaba | 互通 |
+| --- | --- | --- | --- | --- |
+| 通信协议 | rest | spring web/resteasy 编码风格 | 集成feignclient,ribbon
(spring web 编码风格) | 是 |
+| | triple | | | |
+| | dubbo | | | |
+| | grpc | | | |
+| | hessian | | | |
+| 注册中心 | zookeeper | | | |
+| | nacos | 支持 | 支持 | 应用级别注册 |
+
+### 2.dubbo 双注册
+
+ 完成应用级别注册,(dubo2-dubbo3 过度),dubbo版本升级
+
+
+
+
+
+### 3.多协议发布
+
+配置:
+
+ <dubbo:service interface="org.apache.dubbo.samples.DemoService"
protocol="dubbo, grpc,rest"/>
+
+### 4.跨语言
+
+
+
+### 5.多协议交互
+
+
+
+### 6.协议迁移
+
+
+
+rest编码风格
+
+Http协议更通用跨语言调用
+
+dubbo rest 对其他http服务 进行调用
+
+其他httpclient 对dubbo rest进行调用
+
+dubbo restServer 可以与其他web服务,浏览器等客户端直接进行http交互
+
+## consumer TODOLIST(功能已经初步实现,可以调通解析response)
+
+1. org/apache/dubbo/rpc/protocol/rest/RestProtocol.java:157 dynamic load
config
+
+2.org/apache/dubbo/remoting/http/factory/AbstractHttpClientFactory.java:50
load config HttpClientConfig
+
+3.org/apache/dubbo/rpc/protocol/rest/annotation/metadata/MetadataResolver.java:52
support Dubbo style service
+
+4.org/apache/dubbo/remoting/http/restclient/HttpClientRestClient.java:120
TODO config
+
+5.org/apache/dubbo/remoting/http/restclient/HttpClientRestClient.java:140 TODO
close judge
+
+6.org/apache/dubbo/rpc/protocol/rest/message/decode/MultiValueCodec.java:35
TODO java bean get set convert
+
+## provider TODOLIST(待实现)
+
+基于netty实现支持http协议的NettyServer
+
+无注解协议定义
+
+官网场景补充
+
+## Rest使用说明文档及demo:
diff --git a/data/announcements/en/scheduled.yaml
b/data/announcements/en/scheduled.yaml
index efaca2a76f..866f530269 100755
--- a/data/announcements/en/scheduled.yaml
+++ b/data/announcements/en/scheduled.yaml
@@ -50,4 +50,23 @@ announcements:
<a href=
"/en/blog/2023/04/15/advanced-cloud-native-dubbo-3.2-officially-released/">The
brandnew Dubbo 3.2 version</a> <em> is now available!</em>
message: |
We are very happy to announce that Dubbo 3.2 has been officially released!
This version brings many new features and improvements, and it is also an
important attempt of Dubbo in the face of cloud nativeization.<br />
-
+- name: CommunityOverCode 2023
+ startTime: 2023-07-05T00:00:00
+ endTime: 2023-08-20T18:00:00
+ style: >-
+ background-image: linear-gradient(90deg, rgb(249, 145, 6) 0%, rgb(252, 29,
68) 100%);
+ title: |
+ <img alt="ApacheCon-Asia" src ="/imgs/contacts/wechat-account.jpg"
style="float: right; width: 10%; height: auto;" />
+ <a href= "">ApacheCon Asia 2023</a> <em> 8月18 - 8月20日, 北京!</em>
+ message: |
+ Community Over Code 2023 暨 ApacheCon Asia 2023 大会将于 8月18 -
8月20日在北京举办,Apache Dubbo 将带来Mesh、可观测、Kubernetes实践、Serverless部署等 10
多个议题的主题演讲,敬请期待!
+- name: Initializer Release
+ startTime: 2023-06-05T00:00:00
+ endTime: 2023-08-20T18:00:00
+ style: >-
+ background-image: linear-gradient(90deg, rgb(249, 145, 6) 0%, rgb(252, 29,
68) 100%);
+ title: |
+ <img alt="ApacheCon-Asia" src ="/imgs/contacts/wechat-account.jpg"
style="float: right; width: 10%; height: auto;" />
+ <a href= "https://start.dubbo.apache.org/" target="_blank">Dubbo
Initializer</a> <em>officially released!</em>
+ message: |
+ Use Dubbo Initializer to create Dubbo Spring Boot scaffold project in just
1 minute.
diff --git a/data/announcements/zh/scheduled.yaml
b/data/announcements/zh/scheduled.yaml
index 13e8af8f1a..37dfd30a90 100644
--- a/data/announcements/zh/scheduled.yaml
+++ b/data/announcements/zh/scheduled.yaml
@@ -21,3 +21,23 @@ announcements:
message: |
我们非常高兴地宣布,Dubbo 3.2 已经正式发布了!这个版本带来了许多新功能和改进,也是 Dubbo
在面对云原生化的当下的一次重要的尝试。<br /><br />
欢迎扫描右侧二维码关注 Apache Dubbo 微信公众号!
+ - name: CommunityOverCode 2023
+ startTime: 2023-07-05T00:00:00
+ endTime: 2023-08-20T18:00:00
+ style: >-
+ background-image: linear-gradient(90deg, rgb(249, 145, 6) 0%, rgb(252,
29, 68) 100%);
+ title: |
+ <img alt="ApacheCon-Asia" src ="/imgs/contacts/wechat-account.jpg"
style="float: right; width: 10%; height: auto;" />
+ <a href= "">ApacheCon Asia 2023</a> <em> 8月18 - 8月20日, 北京!</em>
+ message: |
+ Community Over Code 2023 暨 ApacheCon Asia 2023 大会将于 8月18 -
8月20日在北京举办,Apache Dubbo 将带来Mesh、可观测、Kubernetes实践、Serverless部署等 10
多个议题的主题演讲,敬请期待!
+ - name: Initializer Release
+ startTime: 2023-06-05T00:00:00
+ endTime: 2023-08-20T18:00:00
+ style: >-
+ background-image: linear-gradient(90deg, rgb(249, 145, 6) 0%, rgb(252,
29, 68) 100%);
+ title: |
+ <img alt="ApacheCon-Asia" src ="/imgs/contacts/wechat-account.jpg"
style="float: right; width: 10%; height: auto;" />
+ <a href= "https://start.dubbo.apache.org/" target="_blank">Dubbo
Initializer</a> <em>项目脚手架正式发布,帮你 1 分钟快速新建应用模板!</em>
+ message: |
+ 使用项目脚手架快速创建 Dubbo Spring Boot 项目模板,帮你解决项目初始化问题。
\ No newline at end of file
diff --git a/layouts/docs/baseof.html b/layouts/docs/baseof.html
index a4e96cce4a..e2cbe533b7 100755
--- a/layouts/docs/baseof.html
+++ b/layouts/docs/baseof.html
@@ -36,8 +36,6 @@
{{ partial "docs/thirdparty-disclaimer.html" . }}
{{- end -}}
{{- end -}}
- {{ partial "page-meta-links.html" . }}
-
<!-- {{ if (and (not .Params.hide_feedback)
(.Site.Params.ui.feedback.enable) (.Site.GoogleAnalytics)) }}-->
<!-- {{ partial "feedback.html" .Site.Params.ui.feedback }}-->
<!-- {{ end }}-->
diff --git a/layouts/partials/toc.html b/layouts/partials/toc.html
index cf77ace3ca..a57cad567b 100755
--- a/layouts/partials/toc.html
+++ b/layouts/partials/toc.html
@@ -1,3 +1,4 @@
+{{ partial "page-meta-links.html" . }}
{{ if not .Params.notoc }}
{{ with .TableOfContents }}
{{ if ge (len .) 100 }}
diff --git a/static/imgs/blog/2023/6/graalvm/aot.png
b/static/imgs/blog/2023/6/graalvm/aot.png
new file mode 100644
index 0000000000..4fa16ad647
Binary files /dev/null and b/static/imgs/blog/2023/6/graalvm/aot.png differ
diff --git a/static/imgs/blog/2023/6/graalvm/demo-1.png
b/static/imgs/blog/2023/6/graalvm/demo-1.png
new file mode 100644
index 0000000000..b300e1f276
Binary files /dev/null and b/static/imgs/blog/2023/6/graalvm/demo-1.png differ
diff --git a/static/imgs/blog/2023/6/graalvm/demo-2.png
b/static/imgs/blog/2023/6/graalvm/demo-2.png
new file mode 100644
index 0000000000..137e6ffb07
Binary files /dev/null and b/static/imgs/blog/2023/6/graalvm/demo-2.png differ
diff --git a/static/imgs/blog/2023/6/graalvm/dubbo-aot-1.png
b/static/imgs/blog/2023/6/graalvm/dubbo-aot-1.png
new file mode 100644
index 0000000000..82a90cbec8
Binary files /dev/null and b/static/imgs/blog/2023/6/graalvm/dubbo-aot-1.png
differ
diff --git a/static/imgs/blog/2023/6/graalvm/dubbo-aot-2.png
b/static/imgs/blog/2023/6/graalvm/dubbo-aot-2.png
new file mode 100644
index 0000000000..76c6b1f7d6
Binary files /dev/null and b/static/imgs/blog/2023/6/graalvm/dubbo-aot-2.png
differ
diff --git a/static/imgs/blog/2023/6/graalvm/graalvm-advantages.png
b/static/imgs/blog/2023/6/graalvm/graalvm-advantages.png
new file mode 100644
index 0000000000..737d30286c
Binary files /dev/null and
b/static/imgs/blog/2023/6/graalvm/graalvm-advantages.png differ
diff --git a/static/imgs/blog/2023/6/graalvm/graalvm-compilation.png
b/static/imgs/blog/2023/6/graalvm/graalvm-compilation.png
new file mode 100644
index 0000000000..d291ca9ae7
Binary files /dev/null and
b/static/imgs/blog/2023/6/graalvm/graalvm-compilation.png differ
diff --git a/static/imgs/blog/2023/6/graalvm/graalvm-copilation2.png
b/static/imgs/blog/2023/6/graalvm/graalvm-copilation2.png
new file mode 100644
index 0000000000..28f481d655
Binary files /dev/null and
b/static/imgs/blog/2023/6/graalvm/graalvm-copilation2.png differ
diff --git a/static/imgs/blog/2023/6/graalvm/graalvm-principal.png
b/static/imgs/blog/2023/6/graalvm/graalvm-principal.png
new file mode 100644
index 0000000000..d0ed097193
Binary files /dev/null and
b/static/imgs/blog/2023/6/graalvm/graalvm-principal.png differ
diff --git a/static/imgs/blog/2023/6/graalvm/language-rank.png
b/static/imgs/blog/2023/6/graalvm/language-rank.png
new file mode 100644
index 0000000000..a60f33ba68
Binary files /dev/null and b/static/imgs/blog/2023/6/graalvm/language-rank.png
differ
diff --git a/static/imgs/blog/2023/6/graalvm/metadata-1.png
b/static/imgs/blog/2023/6/graalvm/metadata-1.png
new file mode 100644
index 0000000000..2048a28c2d
Binary files /dev/null and b/static/imgs/blog/2023/6/graalvm/metadata-1.png
differ
diff --git a/static/imgs/blog/2023/6/graalvm/metadata-2.png
b/static/imgs/blog/2023/6/graalvm/metadata-2.png
new file mode 100644
index 0000000000..374edb70e6
Binary files /dev/null and b/static/imgs/blog/2023/6/graalvm/metadata-2.png
differ
diff --git a/static/imgs/blog/2023/6/graalvm/metadata-3.png
b/static/imgs/blog/2023/6/graalvm/metadata-3.png
new file mode 100644
index 0000000000..918838a555
Binary files /dev/null and b/static/imgs/blog/2023/6/graalvm/metadata-3.png
differ
diff --git a/static/imgs/blog/2023/6/graalvm/serverless-lang-rank.png
b/static/imgs/blog/2023/6/graalvm/serverless-lang-rank.png
new file mode 100644
index 0000000000..07d85c6caf
Binary files /dev/null and
b/static/imgs/blog/2023/6/graalvm/serverless-lang-rank.png differ
diff --git a/static/imgs/blog/2023/6/graalvm/spring-aot.png
b/static/imgs/blog/2023/6/graalvm/spring-aot.png
new file mode 100644
index 0000000000..e972418a6e
Binary files /dev/null and b/static/imgs/blog/2023/6/graalvm/spring-aot.png
differ