Apache Rya 构建并运行示例应用指南

Apache Rya 4.0.1 构建并运行示例应用指南(库形态示例):Maven 演示工程 pom 与源码,MiniAccumuloCluster 伪集群实测 RDF 写入与 SPARQL 查询(JOIN 命中 4 行、COUNT=9)。

Apache RyaApache Rya2026/10/13

Apache Rya 构建并运行示例应用指南

目标:从零构建并运行一个基于 Apache Rya 4.0.1 的最小 RDF 存储示例应用(库形态 SDK 使用示例)。给出完整 pom.xml、示例 Java 源码、编译运行命令与真实回显:MiniAccumuloCluster 伪集群启动、8 条 RDF 三元组写入、SPARQL JOIN 查询命中 4 行、COUNT 查询返回 9。所有命令与输出均在 Ubuntu 22.04 + JDK 8 环境实测通过。

⚠️ 受限前置说明:Apache Rya 是 Java Maven 多模块库(sail/web/dao/spark/extras),无独立 standalone 模式;主存储必须为 Accumulo(含 ZooKeeper、Hadoop 依赖),查询引擎面向 Accumulo 表。本示例采用官方测试封装 MiniAccumuloCluster 伪集群(内置 ZooKeeper + Master + TabletServer + GC)完成全链路实测;正式生产环境需自行部署 Accumulo 1.6.4 + ZooKeeper 3.4.6 + Hadoop 2.5.0,步骤与此类似但不在本示例实测范围内。

一、环境信息

  • 操作系统:Ubuntu 22.04 LTS
  • Java:OpenJDK 1.8.0_504(构建与运行 Rya 4.0.1 必须使用 JDK 8)
  • 构建工具:Apache Maven 3.6.3
  • Rya 版本:4.0.1(依赖 accumulo-core 1.6.4 / hadoop 2.5.0 / RDF4J 2.3.1)
  • 示例工程目录:/opt/rya/rya-demo/
  • 演示账号:root

二、前置条件

安装 JDK 8 并切换环境变量(Rya 4.0.1 需要 JDK 8 字节码,系统默认 JDK 11 会失败):

apt-get update && apt-get install -y openjdk-8-jdk maven
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH
java -version

回显:

openjdk version "1.8.0_504"
OpenJDK Runtime Environment (build 1.8.0_504-8u504-ga-1ubuntu1~22.04.3-b01)
OpenJDK 64-Bit Server VM (build 25.504-b01, mixed mode)
  • Rya 4.0.1 源码需先完成构建(mvn clean install -DskipTests),使 rya.api、rya.sail、accumulo.rya、rya.indexing、rya.test.accumulo 等 4.0.1 构件安装到本地 Maven 仓库 /root/.m2/repository/org/apache/rya/,本示例 pom 才能解析依赖。

三、工程结构

标准 Maven 布局,一个 pom 与两个主类(RyaSparqlDemo 为全流程演示,MiniAccumuloServer 为常驻伪集群启动器):

rya-demo/
├── pom.xml
└── src/main/java/demo/
    ├── RyaSparqlDemo.java
    └── MiniAccumuloServer.java
mkdir -p /opt/rya/rya-demo/src/main/java/demo

四、完整 pom.xml

pom 依赖 Rya 核心模块与 Hadoop 2.5.0,并通过 exec-maven-plugin 绑定默认主类:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>demo</groupId>
    <artifactId>rya-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <rya.version>4.0.1</rya.version>
    </properties>
    <dependencies>
        <dependency><groupId>org.apache.rya</groupId><artifactId>rya.api</artifactId><version>${rya.version}</version></dependency>
        <dependency><groupId>org.apache.rya</groupId><artifactId>rya.sail</artifactId><version>${rya.version}</version></dependency>
        <dependency><groupId>org.apache.rya</groupId><artifactId>accumulo.rya</artifactId><version>${rya.version}</version></dependency>
        <dependency><groupId>org.apache.rya</groupId><artifactId>rya.indexing</artifactId><version>${rya.version}</version></dependency>
        <dependency><groupId>org.apache.rya</groupId><artifactId>rya.test.accumulo</artifactId><version>${rya.version}</version></dependency>
        <dependency><groupId>org.apache.hadoop</groupId><artifactId>hadoop-common</artifactId><version>2.5.0</version></dependency>
        <dependency><groupId>org.apache.hadoop</groupId><artifactId>hadoop-hdfs</artifactId><version>2.5.0</version></dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.6.0</version>
                <configuration>
                    <mainClass>demo.RyaSparqlDemo</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

五、示例源码

创建 /opt/rya/rya-demo/src/main/java/demo/RyaSparqlDemo.java,其完成七步:启动 MiniAccumuloCluster 伪集群 → root 授予 U 可见性标签 → RyaClient 安装实例 → 写入 8 条 RDF 三元组 → 执行 SPARQL JOIN 查询 → 执行 COUNT 查询 → 关闭伪集群。

示例源码:

package demo;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.core.security.Authorizations;
import org.apache.accumulo.minicluster.MiniAccumuloCluster;
import org.apache.accumulo.minicluster.MiniAccumuloConfig;
import org.apache.rya.api.client.Install.InstallConfiguration;
import org.apache.rya.api.client.RyaClient;
import org.apache.rya.api.client.accumulo.AccumuloConnectionDetails;
import org.apache.rya.api.client.accumulo.AccumuloRyaClientFactory;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.ValueFactory;
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
import org.eclipse.rdf4j.query.BindingSet;
import org.eclipse.rdf4j.query.TupleQueryResult;

public class RyaSparqlDemo {

    public static void main(final String[] args) throws Exception {
        final String username = "root";
        final String password = "q1w2e3r4";
        final String ryaInstanceName = "rya_demo";

        // 1) 启动 MiniAccumuloCluster(伪集群:ZooKeeper + 单节点 Accumulo)
        final File miniDataDir = new File("/opt/rya/mini-data");
        final MiniAccumuloConfig cfg = new MiniAccumuloConfig(miniDataDir, password);
        final MiniAccumuloCluster cluster = new MiniAccumuloCluster(cfg);
        cluster.start();
        System.out.println("[Info] Mini Accumulo started. instance=" + cluster.getInstanceName()
                + ", zookeepers=" + cluster.getZooKeepers());

        // 2) 给 root 用户授予 U 可见性标签
        final Connector connector = cluster.getConnector(username, password);
        connector.securityOperations().changeUserAuthorizations(username, new Authorizations("U"));

        // 3) 构建 RyaClient 并安装 Rya 实例(默认配置,不启用附加索引)
        final AccumuloConnectionDetails connectionDetails =
                new AccumuloConnectionDetails(username, password.toCharArray(), cluster.getInstanceName(), cluster.getZooKeepers());
        final RyaClient ryaClient = AccumuloRyaClientFactory.build(connectionDetails, connector);
        ryaClient.getInstall().install(ryaInstanceName, InstallConfiguration.builder().build());
        System.out.println("[Info] Rya instance installed: " + ryaInstanceName);

        // 4) 写入 8 条 RDF 三元组(同事关系 + 工作地点)
        final ValueFactory vf = SimpleValueFactory.getInstance();
        final List<Statement> statements = new ArrayList<Statement>();
        statements.add(vf.createStatement(vf.createIRI("http://Eve"), vf.createIRI("http://talksTo"), vf.createIRI("http://Charlie")));
        statements.add(vf.createStatement(vf.createIRI("http://David"), vf.createIRI("http://talksTo"), vf.createIRI("http://Alice")));
        statements.add(vf.createStatement(vf.createIRI("http://Alice"), vf.createIRI("http://worksAt"), vf.createIRI("http://CoffeeShop")));
        statements.add(vf.createStatement(vf.createIRI("http://Bob"), vf.createIRI("http://worksAt"), vf.createIRI("http://CoffeeShop")));
        statements.add(vf.createStatement(vf.createIRI("http://George"), vf.createIRI("http://talksTo"), vf.createIRI("http://Frank")));
        statements.add(vf.createStatement(vf.createIRI("http://Frank"), vf.createIRI("http://worksAt"), vf.createIRI("http://CoffeeShop")));
        statements.add(vf.createStatement(vf.createIRI("http://Eve"), vf.createIRI("http://talksTo"), vf.createIRI("http://Bob")));
        statements.add(vf.createStatement(vf.createIRI("http://Charlie"), vf.createIRI("http://worksAt"), vf.createIRI("http://CoffeeShop")));
        ryaClient.getLoadStatements().loadStatements(ryaInstanceName, statements);
        System.out.println("[Info] Loaded " + statements.size() + " statements.");

        // 5) 执行 SPARQL 查询:找 与 CoffeeShop 员工交谈的人
        final String sparql =
                "SELECT ?patron ?employee WHERE { " +
                "?patron <http://talksTo> ?employee. " +
                "?employee <http://worksAt> <http://CoffeeShop>. }";
        System.out.println("[Info] Query: " + sparql);
        final TupleQueryResult result = ryaClient.getExecuteSparqlQuery().executeSparqlQuery(ryaInstanceName, sparql);
        System.out.println("[Result] ");
        while (result.hasNext()) {
            final BindingSet bs = result.next();
            System.out.println("  patron=" + bs.getValue("patron") + ", employee=" + bs.getValue("employee"));
        }
        result.close();

        // 6) 计数查询验证
        final TupleQueryResult countResult = ryaClient.getExecuteSparqlQuery().executeSparqlQuery(ryaInstanceName,
                "SELECT (COUNT(?s) AS ?total) WHERE { ?s ?p ?o }");
        if (countResult.hasNext()) {
            System.out.println("[Result] total statements = " + countResult.next().getValue("total"));
        }
        countResult.close();

        // 7) 关闭伪集群
        cluster.stop();
        System.out.println("[Info] Mini Accumulo stopped.");
    }
}

六、编译与运行

export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH
cd /opt/rya/rya-demo

# 编译
mvn -q clean compile

# 生成 classpath 并运行(限制堆内存 256-512MB)
mvn dependency:build-classpath -Dmdep.outputFile=cp.txt -q
java -Xms256m -Xmx512m -cp "$(cat cp.txt):target/classes" demo.RyaSparqlDemo

验证生效堆内存(另开终端):

jps -v | grep MiniAccumuloServer

在 -Xms256m -Xmx512m 下,jps -v 可看到 Java 进程带有 -Xms256m -Xmx512m 生效标记。

⚠️ 注意:pom 中 exec-maven-plugin 配置的 mainClass 会覆盖命令行 -Dexec.mainClass。若需以 mvn exec:java 运行其他主类(如 MiniAccumuloServer),请改用 mvn dependency:build-classpath 输出 classpath 后直接 java -cp 启动(本示例即采用此方式)。

七、运行结果解读

运行成功,程序真实输出(关键行,日志已压噪):

[Info] Mini Accumulo started. instance=miniInstance, zookeepers=localhost:11511
[DEBUG] o.a.rya.accumulo.AccumuloRdfUtils - Creating accumulo table: rya_demospo
[DEBUG] o.a.rya.accumulo.AccumuloRdfUtils - Creating accumulo table: rya_demopo
[DEBUG] o.a.rya.accumulo.AccumuloRdfUtils - Creating accumulo table: rya_demoosp
[DEBUG] o.a.rya.accumulo.AccumuloRdfUtils - Creating accumulo table: rya_demons
[Info] Rya instance installed: rya_demo
[Info] Loaded 8 statements.
[Info] Query: SELECT ?patron ?employee WHERE { ?patron <http://talksTo> ?employee. ?employee <http://worksAt> <http://CoffeeShop>. }
[Result]
  patron=http://David, employee=http://Alice
  patron=http://Eve, employee=http://Bob
  patron=http://Eve, employee=http://Charlie
  patron=http://George, employee=http://Frank
[Result] total statements = "9"^^<http://www.w3.org/2001/XMLSchema#integer>
[Info] Mini Accumulo stopped.

结果解读:

  • 伪集群启动:MiniAccumuloCluster 以实例名 miniInstance 启动,ZooKeeper 绑定随机端口 localhost:11511;安装 Rya 实例时创建 4 张底层表 rya_demospo(SPO 主表)、rya_demopo、rya_demoosp(倒排索引)、rya_demons(命名空间表),表名前缀为默认的 rya_。
  • JOIN 查询:?patron <talksTo> ?employee . ?employee <worksAt> <CoffeeShop> 为两跳 JOIN,命中 4 行——David→Alice、Eve→Bob、Eve→Charlie、George→Frank(其 employee 均在 CoffeeShop 工作);非目标三元组(如 David talksTo Alice 但 Alice 不在结果 employee 侧)不会出现在结果中。
  • COUNT 查询:COUNT(?s) 统计全部三元组返回 9,即 8 条业务三元组 + 1 条 Rya 实例内部元数据三元组(Rya 安装时写入的 rya_demoinstance_details 相关内部数据),属正常行为。
  • 首行 SLF4J "multiple bindings" 警告(logback-classic 与 slf4j-log4j12 并存)不影响功能,压噪配置可移除其一。

八、常驻伪集群模式(供外部客户端连接)

演示用 MiniAccumuloServer 启动伪集群后不退出,打印就绪标记供 RyaShell 等外部客户端连接:

package demo;

import java.io.File;

import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.core.security.Authorizations;
import org.apache.accumulo.minicluster.MiniAccumuloCluster;
import org.apache.accumulo.minicluster.MiniAccumuloConfig;

public class MiniAccumuloServer {

    public static void main(final String[] args) throws Exception {
        final String username = "root";
        final String password = "q1w2e3r4";
        final File miniDataDir = new File("/opt/rya/mini-data");
        final MiniAccumuloConfig cfg = new MiniAccumuloConfig(miniDataDir, password);
        final MiniAccumuloCluster cluster = new MiniAccumuloCluster(cfg);
        cluster.start();
        final Connector connector = cluster.getConnector(username, password);
        connector.securityOperations().changeUserAuthorizations(username, new Authorizations("U"));
        System.out.println("MINI_ACCUMULO_READY instance=" + cluster.getInstanceName() + " zookeepers=" + cluster.getZooKeepers());
        System.out.flush();
        // 保持运行,供外部客户端连接
        Thread.sleep(Long.MAX_VALUE);
    }
}

运行:

cd /opt/rya/rya-demo
mvn -q dependency:build-classpath -Dmdep.outputFile=cp.txt
java -Xms256m -Xmx512m -cp "$(cat cp.txt):target/classes" demo.MiniAccumuloServer

回显:

MINI_ACCUMULO_READY instance=miniInstance zookeepers=localhost:26007

九、注意事项

⚠️ 注意:

  1. 必须使用 JDK 8 编译运行(Rya 4.0.1 构建产物面向 JDK 8 字节码);JDK 11 下部分模块(如编译 maven.compiler.target 为 1.8 之外的配置)会失败。
  2. MiniAccumuloConfig 要求数据目录为空;若目录已存在旧数据,先 rm -rf /opt/rya/mini-data 再启动。
  3. mvn exec:java 时 pom 的 mainClass 配置覆盖 -Dexec.mainClass,切换主类请用 java -cp 直接启动。
  4. 未设置 HADOOP_HOME 时日志出现 java.io.IOException: HADOOP_HOME or hadoop.home.dir are not set(ConfigUtils 内部捕获,不影响安装);设置后日志干净。
  5. 伪集群停止时 ZooKeeper 会打印大量 Connection refused 重连日志,属关闭阶段的正常现象。
  6. Rya 依赖 Accumulo 1.6.4 / ZooKeeper 3.4.6 / Hadoop 2.5.0(root pom.xml 锁定),生产集群部署需与之匹配。

十、常见问题

Q1:运行报 Connection refused 且一直重连? ZooKeeper 客户端重连日志刷屏通常发生在伪集群已停止或正在停止阶段,属正常现象;若启动阶段持续失败,检查数据目录是否为空(需 rm -rf /opt/rya/mini-data 后重试)。

Q2:mvn exec:java 启动的是错误的类? pom 中 exec-maven-plugin 的 mainClass 配置覆盖命令行 -Dexec.mainClass。改用 mvn dependency:build-classpath -Dmdep.outputFile=cp.txt 后 java -cp "$(cat cp.txt):target/classes" <主类> 直接启动。

Q3:日志出现 HADOOP_HOME or hadoop.home.dir are not set? 设置 export HADOOP_HOME=<hadoop目录>(或在 ~/.bashrc 配置)后重启即可;该异常被内部捕获,不阻断安装与查询。

Q4:查询结果为空? 检查写入的可见性:changeUserAuthorizations 授予 U 标签,客户端查询需带 Authorizations("U");本示例 root 用户已授予,无标签三元组不可见时结果为空属正常权限行为。

Q5:COUNT 结果比写入条数多 1? Rya 安装实例时会写入 1 条内部元数据三元组,COUNT(?s) WHERE { ?s ?p ?o } 统计的是全部三元组(业务数据 + 内部数据)。