基于 GraalVM 的 ShardingSphere Proxy Native 探索(上篇)

素年锦时静待君丶
发布于 2023-5-18 11:46
浏览
0收藏

作者简介:ShardingSphere Contributor,何其恒,自2021 年开始为项目贡献小的改进。专注于添加现有模块对 GraalVM Native-Image 的第一方支持与 ShardingSphere 的分片算法类改进。目前,他正在为现有模块的依赖树处理所需的 GraalVM 可达性元数据,并完成项目对 GraalVM Native Build Tools 的集成。

前言

笔者以 Make ShardingSphere Proxy in GraalVM Native Image form available[1] 的主要推动者的上下文身份,旨在通过本文,介绍 GraalVM Native Image 形态的 Apache ShardingSphere Proxy 的起源。

为 Apache ShardingSphere Proxy 创建 GraalVM Native Image 的首要目标,是相对于通过 JVM 启动的 Proxy,实现更快速的启动和更低的资源占用。GraalVM 的 native-image tool 支持将 Java 应用程序通过 ahead-of-time(AOT) 编译为 native executables 或 shared libraries。

传统上,Java 代码是在运行时通过 just-in-time(JIT) 编译的,而 AOT 编译有两个主要优点:首先,它缩短了启动时间,因为代码已经预编译成高效的机器代码。其次,它减少了 Java 应用程序的内存占用,因为它无需包含在运行时加载和优化代码的基础结构。还有其他优势,例如更可预测的性能和更少的 CPU 总使用率。

在 Spring,Quarkus,Micronaut 和 Helidon 等 Web Framework 一侧,此工作的最终目标应当允许 ShardingSphere JDBC Core 及已有可选插件的 SPI 实现的相关依赖可在对应的 Framework 的生态下的 GraalVM Native Image 中使用,以在 ShardingSphere 的混合部署架构下提供同一级别的支持。

这同样为在 ShardingSphere 的 SPI 实现中,GraalVM Truffle Framework 的 Language 实现使用提供性能改善。展望未来,与 GraalVM 生态交互有助于为 OpenJDK 社区的 Project Galahad[2] 的成果做准备。在引出 How to make ShardingSphere Proxy in GraalVM Native Image form available 之前,笔者需要引入一系列 GraalVM 的前置定义,以确保上下文的语义一致。

初识 GraalVM

在 Ubuntu 22.04 的最小安装实例中完成进一步阐释。对于一个全新的 Ubuntu 实例,可以通过 GraalVM JDK Downloader[3] 来完成 GraalVM 的安装,但考虑到更常见的场景,使用者通常需要一个标准的 Hotspot JVM 来作为验证,使用 SDKMAN! 实际是预期行为,当前上下文以 Microsoft OpenJDK 17 作为参照物。

通过 Linux 下的 SDKMAN! 安装 GraalVM CE 22.3.1 首先获得的是 GraalVM just-in-time (JIT) compiler 。这是一个完整的 JVM 实现,任何针对 Hotspot JVM 能实现的事件,在现实场景下都能对其进行,包括但不限于JVM Tool Interface,即 JVMTI 形态的 javaagent 等在 native-image 组件(在 GraalVM 保护伞下被称为 substratevm)和 espresso 组件中不可使用的事件。

在 GraalVM 23.0 CE 之前,由 GraalVM 的 native-image 组件并不由 GraalVM CE 本体直接携带,需要单独通过 GraalVM Updater 安装。由于 Ubuntu 的最小安装实例未默认安装 zip 等组件,还需要手动安装 SDKMAN! 的安装过程中需要的中间依赖,即 unzip ,zip,curl 和 sed。

此外需要额外安装与 local toolchain 相关的系统依赖,各操作系统需要的依赖均在 native-image 的 Reference Manual 的 Prerequisites 一节[4] 被指出。在部分 Ubuntu 版本中,libz-dev 实际不会被安装,而是被 zlib1g-dev 覆盖。

cd /tmp/
sudo apt install unzip zip curl sed -y
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk install java 22.3.1.r17-grl
sdk install java 17.0.6-ms
sdk default java 22.3.1.r17-grl
sdk use java 22.3.1.r17-grl
gu install native-image
sudo apt-get install build-essential libz-dev zlib1g-dev -y

GraalVM CE 本体包含被称为 GraalVM Updater 的命令行工具,在执行 SDKMAN! 的 sdk use java 22.3.1.r17-grl的 bash 命令后,这将作为 $JAVA_HOME/bin/gu 暴露使用。

linghengqian@DESKTOP-TEST:~/.sdkman/candidates/java/22.3.1.r17-grl/bin$ pwd
/home/linghengqian/.sdkman/candidates/java/22.3.1.r17-grl/bin
linghengqian@DESKTOP-TEST:~/.sdkman/candidates/java/22.3.1.r17-grl/bin$ ls
gu         java     javap     jdb        jfr     jinfo  jmod      jrunscript  jstat    native-image            rebuild-images
jar        javac    jcmd      jdeprscan  jhsdb   jlink  jpackage  jshell      jstatd   native-image-configure  rmiregistry
jarsigner  javadoc  jconsole  jdeps      jimage  jmap   jps       jstack      keytool  polyglot                serialver

借助于 GraalVM Updater, 使用者能够接触位于 GraalVM Organization 上所有可安装的组件。其还可通过命令行安装其他本地下载的组件,如 TruffleBF[5],这是 GraalVM Truffle 上的一个 Brainfuck 实现。对于 native-image不支持的架构,可通过 native-image-llvm-backend 的 LLVM 后端自行完成 riscv64 等架构的实现。

linghengqian@DESKTOP-TEST:~$ gu available
Downloading: Component catalog from www.graalvm.org
ComponentId              Version             Component name                Stability                     Origin
---------------------------------------------------------------------------------------------------------------------------------
espresso                 22.3.1              Java on Truffle               Supported                     github.com
espresso-llvm            22.3.1              Java on Truffle LLVM Java librSupported                     github.com
js                       22.3.1              Graal.js                      Supported                     github.com
llvm                     22.3.1              LLVM Runtime Core             Supported                     github.com
llvm-toolchain           22.3.1              LLVM.org toolchain            Supported                     github.com
native-image             22.3.1              Native Image                  Early adopter                 github.com
native-image-llvm-backend22.3.1              Native Image LLVM Backend     Early adopter (experimental)  github.com
nodejs                   22.3.1              Graal.nodejs                  Supported                     github.com
python                   22.3.1              GraalVM Python                Experimental                  github.com
R                        22.3.1              FastR                         Experimental                  github.com
ruby                     22.3.1              TruffleRuby                   Experimental                  github.com
visualvm                 22.3.1              VisualVM                      Experimental                  github.com
wasm                     22.3.1              GraalWasm                     Experimental                  github.com

对于 Apache ShardingSphere Proxy 的 Native Image 化处理,与将 ElasticSearch Server, Apache Kafka Server 和 Apache Zookeeper Server 等中间件实现为 GraalVM Native Image 的处理并没有什么相对较大的差别。这要求找到这些中间件的 mainclass 和对应的 main method,作为目标入口点打包成 GraalVM Native Image。对于 main method 的参数,将作为最终产物的命令行参数被传入。考虑到大多数 Java 中间件在非 Docker Image 环境下的通常启动方式,一般共识认为可以通过 GraalVM Native Image,将 Java 中间件的 Server 端以 CLI 的二进制文件分发。

在 Apache ShardingSphere 社区,最早被记录的为 ShardingSphere JDBC Core 构建 GraalVM Native Image 的 issue 是 Tuyen 报告的 Graalvm native image cause with Groovy 4[6],它引出了将 Apache ShardingSphere Proxy 打包成 GraalVM Native Image 的表层问题,Groovy 并不是 GraalVM Native Image 上的一等公民。Tuyen 认为构建 GraalVM Native Image 失败的原因是 ShardingSphere 的主分支将 Groovy 版本从 V3 切换到 V4 。从 Paul King 调查的 GROOVY-10643[7] 的结果上看,Groovy 4.0.3 解决了问题,但与 IndyInterface 的相关弃用类不是 ShardingSphere 需要关注的主要点。这个问题实际上还可以通过更新 GroovySubstitutions 的类内容[8] 来解决。

为了讨论 Apache ShardingSphere Proxy 在构建 GraalVM Native Image 时遇到的问题,必须引入 GraalVM 对 Reachability Metadata 的定义。对于 GROOVY-10643 涉及到的 GroovySubstitutions fails with groovy 4[9],这里面 Paul King 额外配置了 GraalVM Reachability Metadata 的 reflect-config 部分。InfoQ 的译者在GraalVM 22.2 添加库配置仓库功能[10] 把 GraalVM Reachability Metadata 一词翻译为 GraalVM 可达性元数据 ,这不一定是一个容易理解的翻译,因为 reachable 在 GraalVM 的上下文可以和其他词搭配实现更丰富的语义。

Native Image 是用 Java 编写的,将 Java 字节码作为输入来生成 standalone binary( executable 或 shared library )。针对 Native Image 的初级概念,可直接阅读 Native Image Basics[11]。

基于 GraalVM 的 ShardingSphere Proxy Native 探索(上篇)-鸿蒙开发者社区

JVM 的动态语言特性(包括反射和资源处理)在运行时计算 dynamically-accessed program elements,例如调用的方法或资源 URL。$JAVA_HOME/bin/native-image 工具在构建 native binary 时执行 static analysis 以确定这些动态特征,但它不能总是详尽地预测所有用途。

为确保将这些 elements 包含到 native binary 中,应该向 native-image builder 提供 reachability metadata( 在 GraalVM 文档中往往简称为 metadata )。为 builder 提供 reachability metadata 还可以确保在 runtime 与 third-party libraries 的无缝兼容。metadata 可以通过以下方式提供给 native-image builder:

通过构建 native binary 时在代码中计算 metadata 并将所需 elements 存储到 native binary 的 initial heap
通过提供存储在项目目录中的META-INF/native-image/<group.id>/<artifact.id>的 JSON 文件

涉及到如何为应用程序自动收集元数据的更多信息,应参阅Collect Metadata with the Tracing Agent[12]。Native Image 接受以下类型的 reachability metadata:

基于 GraalVM 的 ShardingSphere Proxy Native 探索(上篇)-鸿蒙开发者社区

对于最简单的 GraalVM Reachability Metadata 的形态,以 reflect-config.json 中的 JSON 对象来说,其可表现为如下形态:

{
  "condition":{"typeReachable":"org.apache.shardingsphere.elasticjob.lite.internal.instance.InstanceService"},
  "name":"org.apache.shardingsphere.elasticjob.infra.handler.sharding.JobInstance",
  "allDeclaredFields":true,
  "methods":[
    {"name":"<init>","parameterTypes":[] }, 
    {"name":"setJobInstanceId","parameterTypes":["java.lang.String"] }, 
    {"name":"setServerIp","parameterTypes":["java.lang.String"] }
  ]
},

此 JSON 对象的含义是,当且仅当 org.apache.shardingsphere.elasticjob.lite.internal.instance.InstanceService是 reachable 的时候,为 GraalVM Native Image 注册 org.apache.shardingsphere.elasticjob.infra.handler.sharding.JobInstance的查找的所有 Declared 字段,并且同时注册 org.apache.shardingsphere.elasticjob.infra.handler.sharding.JobInstance 的三个具有特定参数的 method。

当拍摄线程快照时,这条元数据实际上指在特定线程上,InstanceService 调用了,或InstanceService 调用到的其他中间类调用到了 JobInstance 的所有 Declared 字段和其中的三个方法。因此,此处的 typeReachable 属性并不是只有 InstanceService一种选择,可以为其他的类,最终根据 typeReachable 的类考虑是否在 GraalVM Native Image 中注册 JobInstance。这形态上可以理解为当且仅当的定语。

此 JSON 对象对应于 ShardingSphere ElasticJob 的 JobInstance 类[13] 的一条 GraalVM Reachability Metadata。注册的方法由 Project Lombok 生成到字节码中。

package org.apache.shardingsphere.elasticjob.infra.handler.sharding;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.apache.shardingsphere.elasticjob.infra.env.IpUtils;

import java.lang.management.ManagementFactory;

@Getter
@Setter
@EqualsAndHashCode(of = "jobInstanceId")
public final class JobInstance {
    
    public static final String DELIMITER = "@-@";
    
    private String jobInstanceId;
    
    private String labels;
    
    private String serverIp;
    
    public JobInstance() {
        this(IpUtils.getIp() + DELIMITER + ManagementFactory.getRuntimeMXBean().getName().split("@")[0]);
    }
    
    public JobInstance(final String jobInstanceId) {
        this(jobInstanceId, null);
    }
    
    public JobInstance(final String jobInstanceId, final String labels) {
        this(jobInstanceId, labels, IpUtils.getIp());
    }
    
    public JobInstance(final String jobInstanceId, final String labels, final String serverIp) {
        this.jobInstanceId = jobInstanceId;
        this.labels = labels;
        this.serverIp = serverIp;
    }
}

探索之后的收获

ShardingSphere Proxy Native 探索 GraalVM 

在 2022 年带来了什么?

在 GraalVM CE 21.3 推出的 Condition Metadata 条件元数据事实上改变了 GraalVM Native Image 的游戏规则。

在那之前,由于不能定义 Condition Metadata,所有在 JSON 文件定义的 GraalVM Reachability Metadata 都必定包含在最终 GraalVM Native Image 当中,这导致如果一个库被设计成被其他库使用,或被其他第三方库使用,往往需要额外在被称为 native-image.properties 的文件中单独配置 native-image 组件的 buildArgs 构建参数,以避开一些错误地被定义在 build time 或 run time 完成初始化的类,或者涉及到的类并不存在,导致无法正常完成 GraalVM Native Image 的编译。

而 GraalVM Tracing Agent 对采集 Conditional 形态的 Metadata 的支持从 GraalVM CE 22.1 推出。这使得建立一个 GraalVM Reachability Metadata 的中央仓库成为可能。

从 2022 年 7 月的 GraalVM CE  22.2 开始,GraalVM Reachability Metadata 的中央仓库现在位于 https://github.com/oracle/graalvm-reachability-metadata 。更核心的特性是构建 Native Image 的内存占用更小,这使得从 GraalVM CE 22.2 开始,能够在几乎什么都不用配置的情况下,在仅有 7 GB 内存的 Github Actions 设备上构建 GraalVM Native Image。

在 GraalVM CE 22.2 之前,在 Github Actions 设备上构建 GraalVM Native Image 也属于 GraalVM 文档定义的 expert feature 之一。Matt Raible 在 Use GitHub Actions to Build GraalVM Native Images[14] 分享过相关流程。

当在 Github Actions 上以 Linux 构建 GraalVM Native Image 时,首先需要使用  pier​​otofy/set-swap-space的  GitHub action 组件增加 Github Actions 设备的 Swap Space,将 swap-size-gb属性设为 10。

当在 Github Actions 上以 Windows 构建 GraalVM Native Image 时,首先需要使用 al-cheb/configure-pagefile-action 的  GitHub action 组件配置 Github Actions 设备 Pagefile 的 size 和 location,将 minimum-size 设置为 10GB,将 maximum-size 设置为 12GB。

习惯上认为可通过 PowerShell 执行 

(Get-CimInstance Win32_PageFileUsage).AllocatedBaseSize 

来将 Pagefile 的设置打印到 Github Actions 的 Log 上来查看是否正常。

如果什么都不做,内存不足在 Linux 上的体现就是 java.lang.OutOfMemoryError: GC overhead limit exceeded。对于 Windows ,则会遇到 The command line is too long.这类特定于 Windows Native 开发环境的问题 -- WSL 不受此影响,而 GraalVM Native Build Tools 是在 Maven Plugin 和 Gradle Plugin 级别解决了这种问题。这一切目前成为了过去时,下游的使用者不需要继续考虑相关因素。

GraalVM Native Image 已被公开证明可用于闭源的超大型应用,[15] 一文演示了如何通过仅 Minecracft Server 的 JAR 和对应的 GraalVM Reachability Metadata 的 JSON 文件,在 Github Actions 上构建 Minecracft Server 所需要的 GraalVM Native Image,并启用 UPX compression 进一步压缩了此 binary 的文件大小。从大众的观点来看,使用 UPX compression 不一定是个好主意,目前 GraalVM CE 正在 23.0 后的里程碑上准备提供内置的 compression 支持[16],而现在 UPX compression 对真正的 startup time 和 peak RSS 存在负面影响,这往往意味着更高的内存占用。

启动流程

ShardingSphere Proxy 的启动流程

作为验证的一部分,在 Build GraalVM Native Image nightly for ShardingSphere Proxy[17] 中已为 GraalVM Native Image 形态的 ShardingSphere Proxy 设置了在 Github Actions 的每夜构建。

这个 PR 的核心是引入了 GraalVM Native Build Tools ,它提供了 Maven Plugin 和 Gradle Plugin,以避免 GraalVM 的下游使用者不得不在 shell 脚本中必须使用 if-else 等语法拼接长度相对较长的 bash 命令才能使用 $JAVA_HOME/bin/native-iamge 等命令行工具。

首先阐释 Apache ShardingSphere Proxy 是如何编译和运行的。当然,下游使用者大多数情况下只需要关注 Linux 环境。以 ShardingSphere 5.3.1 来看,我们可以执行如下命令来 clone source code 观察

git clone -b 5.3.1 git@github.com:apache/shardingsphere.git
cd ./shardingsphere/

通常的流程是,我们可以在已设置 JAVA_HOME 的环境变量的前提下,执行./mvnw clean install -Prelease -T1C -DskipTests -Djacoco.skip=true -Dcheckstyle.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true -B来跳过单元测试,单元测试覆盖率统计,源代码风格检查,发布审计,生成 javadoc 这些与我们目标不相关的流程,并在 ./distribution/proxy/下生成 Apache ShardingSphere Proxy 的产物。

最终产物是一个 .tar.gz 文件,内有一个包含启动类的 jar,并包含启动此 jar 需要的全部第三方库和 LICENSE,这些打包内容由 distribution/proxy/pom.xml的 release profile 定义。

启动 jar 同样需要执行长度不短的 $JAVA_HOME/bin/java命令。对于 ShardingSphere Proxy,其具有 distribution/proxy/src/main/resources/bin/start.sh的 shell  脚本文件来简化定义的流程。

SERVER_NAME=ShardingSphere-Proxy

DEPLOY_BIN="$(dirname "${BASH_SOURCE-$0}")"
cd "${DEPLOY_BIN}/../" || exit;
DEPLOY_DIR="$(pwd)"

LOGS_DIR=${DEPLOY_DIR}/logs
if [ ! -d "${LOGS_DIR}" ]; then
    mkdir "${LOGS_DIR}"
fi


STDOUT_FILE=${LOGS_DIR}/stdout.log
EXT_LIB=${DEPLOY_DIR}/ext-lib

CLASS_PATH=".:${DEPLOY_DIR}/lib/*:${EXT_LIB}/*"

if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]];  then
    JAVA="$JAVA_HOME/bin/java"
elif type -p java; then
    JAVA="$(which java)"
else
    echo "Error: JAVA_HOME is not set and java could not be found in PATH." 1>&2
    exit 1
fi

is_openjdk=$($JAVA -version 2>&1 | tail -1 | awk '{print ($1 == "OpenJDK") ? "true" : "false"}')
total_version=$($JAVA -version 2>&1 | grep version | sed '1!d' | sed -e 's/"//g' | awk '{print $3}')
int_version=${total_version%%.*}
if [ "$int_version" = '1' ] ; then
    int_version=${total_version%.*}
    int_version=${int_version:2}
fi
echo "we find java version: java${int_version}, full_version=${total_version}, full_path=$JAVA"

case "$OSTYPE" in
*solaris*)
  GREP=/usr/xpg4/bin/grep
  ;;
*)
  GREP=grep
  ;;
esac

VERSION_OPTS=""
if [ "$int_version" = '8' ] ; then
    VERSION_OPTS="-XX:+UseConcMarkSweepGC -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70"
elif [ "$int_version" = '11' ] ; then
    VERSION_OPTS="-XX:+SegmentedCodeCache -XX:+AggressiveHeap"
    if $is_openjdk; then
      VERSION_OPTS="$VERSION_OPTS -XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler"
    fi
elif [ "$int_version" = '17' ] ; then
    VERSION_OPTS="-XX:+SegmentedCodeCache -XX:+AggressiveHeap"
else
    echo "unadapted java version, please notice..."
fi

DEFAULT_CGROUP_MEM_OPTS=""
if [ "$int_version" = '8' ] ; then
        DEFAULT_CGROUP_MEM_OPTS=" -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:InitialRAMPercentage=80.0 -XX:MinRAMPercentage=80.0 -XX:MaxRAMPercentage=80.0 "
else
        DEFAULT_CGROUP_MEM_OPTS=" -XX:InitialRAMPercentage=80.0 -XX:MinRAMPercentage=80.0 -XX:MaxRAMPercentage=80.0 "
fi

CGROUP_MEM_OPTS="${CGROUP_MEM_OPTS:-${DEFAULT_CGROUP_MEM_OPTS}}"

JAVA_OPTS=" -Djava.awt.headless=true "

DEFAULT_JAVA_MEM_COMMON_OPTS=" -Xmx2g -Xms2g -Xmn1g "
if [ -n "${IS_DOCKER}" ]; then
        JAVA_MEM_COMMON_OPTS="${CGROUP_MEM_OPTS}"
else
        JAVA_MEM_COMMON_OPTS="${JAVA_MEM_COMMON_OPTS:-${DEFAULT_JAVA_MEM_COMMON_OPTS}}"
fi

JAVA_MEM_OPTS=" -server ${JAVA_MEM_COMMON_OPTS} -Xss1m -XX:AutoBoxCacheMax=4096 -XX:+UseNUMA -XX:+DisableExplicitGC -XX:LargePageSizeInBytes=128m ${VERSION_OPTS} -Dio.netty.leakDetection.level=DISABLED "




MAIN_CLASS=org.apache.shardingsphere.proxy.Bootstrap

unset -v PORT
unset -v ADDRESSES
unset -v CONF_PATH
unset -v FORCE

print_usage() {
    echo "usage:"
    echo "start.sh [port] [config_dir]"
    echo "  port: proxy listen port, default is 3307"
    echo "  config_dir: proxy config directory, default is 'conf'"
    echo ""
    echo "start.sh [-a addresses] [-p port] [-c /path/to/conf]"
    echo "The options are unordered."
    echo "-a  Bind addresses, can be IPv4, IPv6, hostname. In"
    echo "    case more than one address is specified in a"
    echo "    comma-separated list. The default value is '0.0.0.0'."
    echo "-p  Bind port, default is '3307', which could be changed in server.yaml"
    echo "-c  Path to config directory of ShardingSphere-Proxy, default is 'conf'"
    echo "-f  Force start ShardingSphere-Proxy"
    exit 0
}

if [ "$1" == "-h" ] || [ "$1" == "--help" ] ; then
    print_usage
fi

print_version() {
    $JAVA ${JAVA_OPTS} ${JAVA_MEM_OPTS} -classpath ${CLASS_PATH} org.apache.shardingsphere.infra.autogen.version.ShardingSphereVersion
    exit 0
}

if [ "$1" == "-v" ] || [ "$1" == "--version" ] ; then
    print_version
fi

if [ $# == 0 ]; then
    CLASS_PATH=${DEPLOY_DIR}/conf:${CLASS_PATH}
fi

if [[ $1 == -a ]] || [[ $1 == -p ]] || [[ $1 == -c ]] || [[ $1 == -f ]] ; then
    while getopts ":a:p:c:f" opt
    do
        case $opt in
        a)
          echo "The address is $OPTARG"
          ADDRESSES=$OPTARG;;
        p)
          echo "The port is $OPTARG"
          PORT=$OPTARG;;
        c)
          echo "The configuration path is $OPTARG"
          CONF_PATH=$OPTARG;;
        f)
          echo "The force param is true"
          FORCE=true;;
        ?)
          print_usage;;
        esac
    done

elif [ $# == 1 ]; then
    PORT=$1
    echo "The port is $1"

elif [ $# == 2 ]; then
    PORT=$1
    CONF_PATH=$2
    echo "The port is $1"
    echo "The configuration path is $2"
fi

if [ -z "$CONF_PATH" ]; then
    CONF_PATH=${DEPLOY_DIR}/conf
fi

if [ -z "$PORT" ]; then
    PORT=-1
fi

if [ -z "$ADDRESSES" ]; then
    ADDRESSES="0.0.0.0"
fi

if [ -z "$FORCE" ]; then
    FORCE=false
fi

CLASS_PATH=${CONF_PATH}:${CLASS_PATH}
MAIN_CLASS="${MAIN_CLASS} ${PORT} ${CONF_PATH} ${ADDRESSES} ${FORCE}"

echo "The classpath is ${CLASS_PATH}"
echo "main class ${MAIN_CLASS}"

if [ -n "${IS_DOCKER}" ]; then
  exec $JAVA ${JAVA_OPTS} ${JAVA_MEM_OPTS} -classpath ${CLASS_PATH} ${MAIN_CLASS}
  exit 0
fi

echo -e "Starting the $SERVER_NAME ...\c"

nohup $JAVA ${JAVA_OPTS} ${JAVA_MEM_OPTS} -classpath ${CLASS_PATH} ${MAIN_CLASS} >> ${STDOUT_FILE} 2>&1 &
if [ $? -eq 0 ]; then
  case "$OSTYPE" in
  *solaris*)
    pid=$(/bin/echo "${!}\\c")
    ;;
  *)
    pid=$(/bin/echo -n $!)
    ;;
  esac
  if [ $? -eq 0 ]; then
      sleep 1;
      if ps -p "${pid}" > /dev/null 2>&1; then
        echo " PID: $pid"
        echo "Please check the STDOUT file: $STDOUT_FILE"
        exit 0
      fi
  else
    echo " FAILED TO GET PID"
  fi
else
  echo " SERVER DID NOT START"
fi
echo "Please check the STDOUT file: $STDOUT_FILE"
exit 1

不讨论用于中止 ShardingSphere Proxy 运行的 distribution/proxy/src/main/resources/bin/stop.sh 的内容。当前,可以首先假设在 JDK17,非 Docker Image 环境,不启用 ShardingSphere Agent ,不指定 ShardingSphere 的配置文件夹,也不指定额外的命令行参数的前提下来粗略定义最终的启动命令如下,当然考虑到$(pwd)得到的当前路径需要另外定义 export DEPLOY_DIR="$(pwd)",这条命令不能直接使用。

nohup $JAVA_HOME/bin/java -Djava.awt.headless=true -server -Xmx2g -Xms2g -Xmn1g -Xss1m -XX:AutoBoxCacheMax=4096 -XX:+UseNUMA -XX:+DisableExplicitGC -XX:LargePageSizeInBytes=128m -XX:+SegmentedCodeCache -XX:+AggressiveHeap -Dio.netty.leakDetection.level=DISABLED -classpath $(pwd)/conf:.:$(pwd)/lib/*:$(pwd)/ext-lib/ org.apache.shardingsphere.proxy.Bootstrap -1 $(pwd)/conf "0.0.0.0" false >> $(pwd)/logs/stdout.log 2>&1 &

额外撇开 JVM 参数 -- 在构建 GraalVM Native Image 的 buildArg 处理繁琐的 JVM 参数不一定合理,而 GraalVM Native Image 的二进制文件虽然能够使用 JVM 参数,但显然没那么多,这条命令可以做简化。

java -classpath $(pwd)/conf:.:$(pwd)/lib/*:$(pwd)/ext-lib/ org.apache.shardingsphere.proxy.Bootstrap -1 $(pwd)/conf "0.0.0.0" false

这实际就是在构建完 GraalVM Native Image 形态的 ShardingSphere Proxy 之后,需要考虑的命令行参数的主要内容, org.apache.shardingsphere.proxy.Bootstrap 是启动类。

我们只需要考虑四个命令行参数,对应为-1的 PORT,对应为$(pwd)/conf的 CONF_PATH,对应为 "0.0.0.0"的 ADDRESSES,对于为false的 FORCE。

org.apache.shardingsphere.proxy.Bootstrap 的内容无需查看, distribution/proxy/src/main/resources/bin/start.sh为这四个参数标记了说明。PORT 为 ShardingSphere Proxy 的监听端口,CONF_PATH 为 ShardingSphere Proxy 的配置文件所在的绝对路径,ADDRESSES 为 ShardingSphere Proxy 监听的 IP 地址,FORCE 即为 Force Start,如果为 true 则保证 ShardingSphere Proxy Native 无论能否连接都能正常启动。对于 GraalVM Native Image,classpath 的内容不应考虑,GraalVM Native Image 存在 closed-world assumption,构建 GraalVM Native Image 时没使用到的 JAR 之后同样也不应考虑,在下文将引入 GraalVM Truffle Espresso 的讨论。

Apache ShardingSphere Proxy 实际上也是使用 Maven 构建的,其只使用如下的依赖。在 shell 脚本使用到的 org.apache.shardingsphere.proxy.Bootstrap 类也是在

 org.apache.shardingsphere:shardingsphere-proxy-bootstrap中定义的。

由 release profile 定义中的 maven-assembly-plugin的 maven execution 将会依赖特定的要求构建出 .tar.gz的产物。

基于 GraalVM 的 ShardingSphere Proxy Native 探索(上篇)-鸿蒙开发者社区



文章转载自公众号: ShardingSphere官微


分类
已于2023-5-18 11:46:13修改
收藏
回复
举报
回复
    相关推荐