65.9K
CodeProject 正在变化。 阅读更多。
Home

VisionFive 2 RISC-V - 设置 Apache2、Php、Postgres、Mosquitto 和编译器

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2023年12月16日

CPOL

21分钟阅读

viewsIcon

6365

使用 Apache2、PHP 和 Postgres 创建一个简单的 VisionFive 2 RISC-V SBC 网站,并集成 Mosquitto 消息代理,同时安装 Golang、Java 和 Rust。

目录

引言

VisionFive 2 SBC 在尺寸和功能上与 Raspberry Pi 4 相似,但与使用 ARM 处理器技术的 Raspberry Pi 4 不同,VisionFive 2 搭载的 JH7110 处理器使用 RISC-V 处理器技术。VisionFive 2 的支持社区不大,在互联网上查找与架构相关的帮助和文档可能困难且耗时。

由于 RISC-V 处理器技术相对较新,设备种类较少,因此 RISC-V 可用的软件种类和健壮性不如 Raspberry Pi 4,尤其是在物联网领域。幸运的是,有大量应用程序以某种方式依赖于 C/C++ 工具链,只要 RISC-V 的工具链正确,那么使用该工具链构建的应用程序往往是可靠的。

一个明显的弱点似乎是 GPU 固件,这意味着图形应用程序(如网页浏览器)可能会遇到问题,直到 GPU 固件和图形堆栈的各个层面变得更加健壮和可靠。

然而,Apache2 HTTP Web 服务器以及PHP 脚本语言PostgreSQL 数据库引擎都有 RISC-V 版本,并且可以使用 apt install 在安装了 Debian 操作系统的 VisionFive 2 上安装。我自然很好奇,决定尝试将我最近购买的 VisionFive 2 变成一个网络服务器,使用类似 LAMP 的 Apache2、PHP 和 PostgreSQL 作为数据库引擎来创建一个简单的网站。我选择了 PostgreSQL 而非 MySQL,因为我找到了一篇简短的文章,比较了几种为 RISC-V 编译的数据库引擎,据报道 PostgreSQL 更稳定。

我还想在 VisionFive 2 上运行一些消息代理的实验。有几种不同的消息代理协议,我决定从Eclipse 项目 Mosquitto 开始。Mosquitto 使用发布/订阅模型,其中客户端将消息发布到特定主题,其他客户端订阅该主题以接收这些消息。

最后,我好奇有哪些编程语言可用,以及它们如何与一个简单的应用程序配合工作。

背景

关于我最初使用 VisionFive 2 的工作背景,我推荐我之前写的文章《设置新的 VisionFive 2 RISC-V SBC》,其中描述了设备的开箱和功能探索。那篇文章中一个重要的话题是解决我发现的 64 GB microSD 卡文件空间仅有 3.2 GB 的问题。在那篇文章中,我描述了使用 Debian 的 parted 和 resize2fs 工具来调整包含文件系统的分区大小,在我的例子中,从 3.2 GB 扩展到近 64 GB。

软件安装和设置

所有感兴趣的软件包都可以通过 apt 进行安装,但在某些情况下,初始软件包安装之后需要使用其他工具来安装额外的功能。在某些情况下,安装后需要手动修改配置文件。

Apache 安装和设置

Apache2 的安装过程非常顺利,安装脚本似乎处理了所有必要的事情。我能够通过在 Windows PC 上使用 URL http://starfive/ 的浏览器来测试 Apache2 网络服务器,并显示了默认的 Apache2 测试页面。

sudo apt install apache2

Apache2 在给定 URL 时查找网页的服务器目录是 /var/www/html。我做的第一件事就是从我的主目录创建一个符号链接到 /var/www/html,以便我方便访问它。

Apache2 的配置文件和目录位于 /etc/apache2。如果您决定通过 Apache2 网络服务器提供额外的服务,您将需要修改目录树(根目录是 /etc/apache2)中的文件。对于某些服务,您还需要安装额外的软件包。请参阅Digital Ocean:如何在 Ubuntu 或 Debian VPS 上配置 Apache 网络服务器,以获取 Apache2 所使用的文件和目录结构的概述和一些详细信息。

有几个 Apache2 服务管理命令很有用

  • sudo systemctl status apache2 显示服务当前状态
  • sudo systemctl restart apache2 重启服务,通常在配置文件更改后使用
  • sudo systemctl start apache2 启动服务,很少使用,因为 Apache2 在启动时会自动启动
  • sudo systemctl stop apache2 停止服务

PHP 安装和设置

PHP 的安装也顺利进行。我同时安装了 PHP 和 Apache2 模块,以便在 Apache2 网络服务器或命令行上使用 PHP 脚本。

sudo apt install php php-cli libapache2-mod-php

使用 php --version 快速版本检查显示 PHP 8.1.12 (cli) (built: Nov 10 2022 07:28:34) (NTS)

安装 PHP 后,我测试了一个快速 PHP 脚本以检查它是否正常工作。脚本在一个名为 test.php 的文件中,其源代码如下。第一行导致脚本由 PHP 解释器执行,因此它包含井号 (#) 后跟感叹号 (!),这会告诉 shell 这是一个要由感叹号后面路径的程序执行的文件,在本例中是位于 /usr/bin 的 PHP 解释器。

#!/usr/bin/php
<?php
print "hello world\n"
?>

我使用 vi 文本编辑器创建文件,并使用 chmod 命令使文件 test.php 可执行,然后使用 ./test.php 执行该文件,因为要执行的文件在当前目录中。脚本生成了预期的输出。

user@starfive:~/Documents/github/web/phpdb$ vi test.php
user@starfive:~/Documents/github/web/phpdb$ chmod +x test.php
user@starfive:~/Documents/github/web/phpdb$ ./test.php
hello world
user@starfive:~/Documents/github/web/phpdb$

接下来,我在 Apache2 HTML 文件夹中尝试了一个简单的 PHP 脚本,以测试 PHP 是否与 Apache2 协同工作。测试是使用 PHP 内置函数 phpinfo() 显示一个包含各种 PHP 设置的网页。浏览器中使用的 URL 是 http://starfive/first.php

user@starfive:~/Documents/webserver$ vi first.php
user@starfive:~/Documents/webserver$ cat first.php
<?php
phpinfo();
?>
user@starfive:~/Documents/webserver$ 

PostgreSQL 安装和设置

我发现 PostgreSQL 的安装和设置是这些服务中最复杂的。事实上,我不得不 apt remove 我第一次安装尝试的软件包,因为我似乎犯了一个错误,导致无法进一步进行。第二次尝试安装 PostgreSQL 相当顺利,因为我已经犯了足够的错误并从中吸取了教训。

各种编程语言都有自己的库或包,用于访问 PostgreSQL 服务和执行数据库操作。大多数语言都有不止一个,其中几种语言具有一个或多个版本的对象关系映射(ORM)软件,这些软件位于标准数据库连接和 SQL 语句执行库之上。

我在设置操作环境时犯的一个错误是没有理解 psql 工具的使用以及 pg_hba.conf 配置文件如何影响用户角色。我花了很多时间在不同的帖子和文章之间跳转,每篇文章都提供了必要知识和理解的片段。

以下并非 PostgreSQL 的权威指南,而是我用于让 PostgreSQL 数据库引擎充当简单数据库存储库的程序,我可以使用 PHP 访问该数据库以即时构建网页。

在安装和设置的某些阶段,可能需要重新启动 PostgreSQL 服务。要重新启动服务,请使用 service 命令,并指定 stopstart 选项来停止或启动 PostgreSQL 服务。最常见的需要重新启动 PostgreSQL 服务的时间是更改配置文件(如 /etc/postgresql/15/main/pg_hba.conf)时。

sudo service postgresql stop
sudo service postgresql start

有时您还需要使用作为 PostgreSQL 安装一部分的 psql 工具来进行更改或检查或修改 PostgreSQL 引擎数据,例如用户/角色设置、数据库或表,或者查看表中的数据。安装时创建的默认超级用户角色/帐户是 postgres,因此过程是使用 sudo 作为用户 postgres 运行 psql 工具。

sudo -u postgres psql

psql 工具简介

psql 工具允许使用 SQL 语句执行查询和 SQL 语言支持的其他操作。它还有一组非 SQL 命令,这些命令要求以反斜杠开头,以便 psql 工具知道该命令不是 SQL 语句,而是 psql 工具命令。

一些比较有用的 psql 反斜杠命令

  • \du - 描述或列出所有用户
  • \l - 列出所有数据库
  • \c dbn - 连接到名为 dbn 的数据库
  • \dv - 描述或列出所有视图
  • \dt - 描述或列出所有表,必须连接到数据库
  • \d [tn] - 描述名为 tn 的表,包括列详细信息,必须连接到数据库
  • \d+ [tn] - 描述名为 tn 的表的附加详细信息,必须连接到数据库
  • \q - 退出 psql 工具

SQL 命令可以从 psql 命令行使用。最有用的是用于 CREATE 角色、ALTER 角色和 GRANT 权限给角色的 SQL 命令。SQL 命令必须以分号 (;) 结尾,而反斜杠命令则不需要。

psql 工具用于添加或修改用户/角色,此操作可能还需要修改 pg_hba.conf 配置文件,如果添加的角色需要特殊处理。配置文件更改后应重启 PostgreSQL 服务。

PostgreSQL 引擎管理由超级用户角色(如 postgres)完成。普通用户角色可以被分配超级用户角色的某些权限,但是超级用户帐户拥有所有权限,除非使用 ALTERGRANT 移除权限。

安装过程简述

第一项任务是使用 sudo apt install 首先安装 PostgreSQL 数据库引擎,其次是 PHP 程序使用该引擎所需的 PHP 驱动程序。其他编程语言通常会有自己的 PostgreSQL 驱动程序版本,例如 Java 的 JDBC 驱动程序,Rust 的 PostgreSQL crate 等。

sudo apt-get install postgresql postgresql-client postgresql-client-common postgresql-common postgresql-contrib
sudo apt install php-pgsql

我犯的一个错误是,一开始就尝试使用 PHP PEAR。我遇到了来自已安装 PEAR 组件的脚本错误。根据我非常有限的经验,似乎一些 PEAR 材料是使用 PHP 8 中已弃用功能的 PHP 5 代码。我发现包 php-pgsql 中的 PostgreSQL 特定接口函数工作正常。

安装 PostgreSQL 软件包后,使用 psql 工具创建几个用户/角色。我创建了两个不同的角色,一个名为 user,它是 VisionFive 2 Debian OS 映像的默认用户名,另一个名为 php,用于 Apache2 网络服务器提供的 PHP Web 应用程序。后来我添加了一个额外的用户/角色 golang,密码为 golang,用于我用 Go 编写的 PostgreSQL 查询应用程序,因为我使用没有密码的 php 用户名时遇到了问题。

通过此安装,您可以获得作为服务运行的 PostgreSQL 数据库引擎以及许多有用的命令行实用程序。

Mosquitto 安装和设置

对于 Mosquitto,我们对使用几个组件感兴趣。首先,我们需要实际的消息代理服务器本身,它为发布者和订阅者客户端提供了一个中心点来会合。Mosquitto 命令行 MQTT 客户端可能很有用,我们需要开发组件,以便我们可以开发自己的客户端应用程序。

 sudo apt install mosquitto mosquitto-clients mosquitto-dev

除了 Mosquitto 消息代理所需的文件外,还有另外两个软件包与 MQTT 消息代理客户端的示例 C 源文件一起使用。我们使用 JSON 库,以便在发布者和订阅者客户端之间发送/接收 JSON 文本消息。为了订阅者的持久存储,我们使用 SQLite3 嵌入式数据库。

第一个是libfastjson JSON 解析器库,另一个是 SQLite3 嵌入式数据库引擎。

sudo apt-get install libfastjson-dev libsqlite3-dev

Mosquitto 消息代理作为服务运行,并在 Linux 启动和初始化时启动。

发现的编程语言

我在 VisionFive 2 上发现了许多可用的编程语言包,其中包含 Debian 桌面。除了 Python3、C/C++ 和 PHP 之外,我还发现了 Go、Rust 和 Java。

Python3 似乎是镜像的一部分,但其他语言需要安装。安装用于 C/C++ 的 gcc 和安装 golang 都很直接。安装 Java 有点复杂,所以我提供了一个关于安装和设置该编程语言的部分。

Golang 安装

通过 apt 命令可以获得 golang 包,因此安装 Golang 最简单的方法是使用 sudo apt install golang

本文档解释了设置 Golang 环境和编写您的第一个 Hello World 应用程序的步骤,请参阅教程:Go 入门

与 Rust 有一个有趣的相似之处,即有一个 go mod init 命令来创建和初始化一个 Go 源模块,这有点类似于 Rust 的 cargo

Golang 与 PostgreSQL

这是使用 Go 访问 PostgreSQL 数据库表的示例程序。您可以使用 go run 进行测试。我尝试使用不需要密码的 PHP PostgreSQL 用户/角色。但是,我无法弄清楚如何在没有密码的情况下进行操作,所以我最终创建了一个新的用户/角色 golang,密码为 golang,以便测试这个简单的查询应用程序。

package main

import (
    "database/sql"
    "fmt"
    _ "github.com/lib/pq"
)

// In order to allow this program to access the database and table
// use the psql utility of PostgreSQL to add the user and to grant
// access to the specified database and table.
//    sudo -u postgres psql
//    create user golang with password 'golang';
//    \c dbtest
//    grant select on phonebook to golang;

const (
    host     = "localhost"
    port     = 5432
    user     = "golang"
    password = "golang"
    dbname   = "dbtest"
)

func main() {
        // connection string
    psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname)

        // open database
    db, err := sql.Open("postgres", psqlconn)
    CheckError(err)

        rows, err := db.Query(`SELECT "firstname", "lastname" FROM "phonebook"`)
        CheckError(err)

        defer rows.Close()
        for rows.Next() {
                var firstname string
                var lastname string

                err = rows.Scan(&firstname, &lastname)
                CheckError(err)

                fmt.Println(firstname, lastname)
        }

        // close database
    defer db.Close()

        // check db
    err = db.Ping()
    CheckError(err)

    fmt.Println("Connected!")
}

func CheckError(err error) {
    if err != nil {
        panic(err)
    }
}

Java 安装

在 Reddit 上 RISC-V 子版块看到一篇关于 openJDK-19 或更早版本在 RISC-V 平台上性能问题的帖子后,我决定研究 VisionFive 2 上的 Java。截至目前,2023 年 12 月 15 日,似乎 openJDK-21 与 Hotspot JIT 已可用。

当我尝试安装 sudo apt install openjdk-21-jre 时,软件包安装过程中显示了错误消息

Setting up ca-certificates-java (20220719) ...
Exception in thread "main" java.lang.InternalError: Error loading java.security                                      file
        at java.base/java.security.Security.initialize(Security.java:105)
        at java.base/java.security.Security.lambda$static$0(Security.java:84)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:319)
        at java.base/java.security.Security.<clinit>(Security.java:83)

随后是许多错误处理包错误,并以以下内容结尾

Errors were encountered while processing:
ca-certificates-java
openjdk-21-jre-headless:riscv64
openjdk-21-jre:riscv64
openjdk-21-jdk-headless:riscv64
openjdk-21-jdk:riscv64
E: Sub-process /usr/bin/dpkg returned an error code (1)
user@starfive:/$

当我尝试安装 sudo apt install openjdk-21-jdksudo apt install openjdk-21-jre-headless 时,也遇到了类似的错误。搜索后,我找到了这篇描述类似问题的帖子,Bug#1030129: ca-certificates-java - 无法与 OpenJDK 21 一起安装:加载 java.security 文件出错,有人回复了一个解决方案,我尝试了,即 sudo apt install ca-certificates-java default-jdk openjdk-21-jdk

最后这个 apt 命令行似乎运行良好,我没有看到任何错误。当我用 java --version 尝试 Java 编译器时,它报告了以下内容

user@starfive:/$ java --version
openjdk 21-ea 2023-09-19
OpenJDK Runtime Environment (build 21-ea+3-Debian-1)
OpenJDK 64-Bit Server VM (build 21-ea+3-Debian-1, mixed mode, sharing)
user@starfive:/$

根据这篇Digital Ocean 文章《如何安装 Java》中名为“步骤 2:管理 Java”的部分,我随后使用 sudo update-alternatives --config javasudo update-alternatives --config javac 检查了 Java 版本和默认使用的版本。我收到了以下输出

user@starfive:/$ sudo update-alternatives --config java
[sudo] password for user:
There are 2 choices for the alternative java (providing /usr/bin/java).

  Selection    Path                                           Priority   Status
------------------------------------------------------------
* 0            /usr/lib/jvm/java-21-openjdk-riscv64/bin/java   2111      auto mode
  1            /usr/lib/jvm/java-17-openjdk-riscv64/bin/java   1704      manual mode
  2            /usr/lib/jvm/java-21-openjdk-riscv64/bin/java   2111      manual mode

Press <enter> to keep the current choice[*], or type selection number:
user@starfive:/$ sudo update-alternatives --config javac
There are 2 choices for the alternative javac (providing /usr/bin/javac).

  Selection    Path                                            Priority   Status
------------------------------------------------------------
* 0            /usr/lib/jvm/java-21-openjdk-riscv64/bin/javac   2111      auto mode
  1            /usr/lib/jvm/java-17-openjdk-riscv64/bin/javac   1704      manual mode
  2            /usr/lib/jvm/java-21-openjdk-riscv64/bin/javac   2111      manual mode

Press <enter> to keep the current choice[*], or type selection number:
user@starfive:/$

另请参阅“步骤 3:设置 JAVA_HOME 环境变量”部分,了解如何将 JAVA_HOME 环境变量添加到 /etc/environment 文件中,以便依赖它的 Java 应用程序能够找到正确的 Java 虚拟机。

在我的安装中,我使用了 which 命令和 ls -l 命令来检查 java 命令的来源

user@starfive:/$ ls -l `which java`
lrwxrwxrwx 1 root root 22 Dec 22  2022 /usr/bin/java -> /etc/alternatives/java
user@starfive:/$ ls /etc/alternatives/java
/etc/alternatives/java
user@starfive:/$ ls -l /etc/alternatives/java
lrwxrwxrwx 1 root root 45 Dec 22  2022 /etc/alternatives/java -> /usr/lib/jvm/java-21-openjdk-riscv64/bin/java
user@starfive:/$ ls -l `which javac`
lrwxrwxrwx 1 root root 23 Dec 22  2022 /usr/bin/javac -> /etc/alternatives/javac
user@starfive:/$ ls -l /etc/alternatives/javac
lrwxrwxrwx 1 root root 46 Dec 22  2022 /etc/alternatives/javac -> /usr/lib/jvm/java-21-openjdk-riscv64/bin/javac
user@starfive:/$

由于 Gradle 是 Java 的标准构建工具,我们继续使用 sudo apt install gradle 来安装它。这将提供 groovy 编程语言以及 ant。我们还使用 sudo apt install maven 来安装 Maven,因为它也是 Java 使用的构建系统。既然如此,我们继续使用 sudo apt install kotlin 来安装 kotlin

JDBC 安装和 PostgreSQL JDBC 驱动

既然我们正在将 PostgreSQL 数据库引擎与 PHP 和 Golang 一起使用,那么安装 Java 程序与我们正在使用 PHP 和 Golang 示例访问的相同电话簿表进行交互所需的组件也是有意义的。

首先,让我们看看现有的 .jar 文件及其位置

user@starfive:~/Documents$ find . / -iname "*.jar" 2>/dev/null
/usr/share/java/java-atk-wrapper.jar
/usr/share/java/libintl-0.21.jar
/usr/share/java/libintl.jar
/usr/share/maven-repo/org/gnu/gettext/libintl/debian/libintl-debian.jar
/usr/share/maven-repo/org/gnu/gettext/libintl/0.21/libintl-0.21.jar
/usr/share/ca-certificates-java/ca-certificates-java.jar
/usr/lib/jvm/java-21-openjdk-riscv64/lib/jrt-fs.jar
/usr/lib/jvm/java-17-openjdk-riscv64/lib/jrt-fs.jar
user@starfive:~/Documents$

接下来,使用 sudo apt install libpostgresql-jdbc-java 安装 PostgreSQL 的 JDBC 驱动程序,然后用一个简单的 HelloWorld.java 程序进行尝试,该程序尝试连接到 PostgreSQL。

这是我们访问 PostgreSQL 数据库 dbtest 及其中的电话簿表的简单 Java 程序。

import java.sql.*;

class HelloWorld {

    // test making a database connection to the PostgreSQL database
    // see also Stackoverflow post
    //   https://stackoverflow.com/questions/18288058/how-is-driver-class-located-in-jdbc4
    public static void dbtest_test () {
        try {
           // we specify the same PHP PostgreSQL user as we are using
           // with out PHP scripts as it does not need a password.
           // we are also using the same phonebook table from the
           // dbtest database we created with PHP and Golang.
           Connection con = DriverManager.getConnection(
               "jdbc:postgresql://:5432/dbtest","php","");
           //here dbtest is database name, php is username with no password
           Statement stmt=con.createStatement();
           ResultSet rs=stmt.executeQuery("select * from phonebook");
           while(rs.next())
               System.out.println(rs.getString(1) + "  " + rs.getString(2) + " " + rs.getString(3));
           con.close();
        } catch(Exception e) {
             System.out.println(e);
        }
    }

    // main entry point
    public static void main(String[] args) {
        System.out.println("Hello, World!");

        try {
                dbtest_test();
        } catch(Exception e) {
             System.out.println(e);
        }
    }
}

现在我们尝试编译和运行它。

user@starfive:~/Documents$ javac HelloWorld.java
user@starfive:~/Documents$ java HelloWorld
Hello, World!
java.sql.SQLException: No suitable driver found for jdbc:postgresql://:5432/dbtest
user@starfive:~/Documents$ find . / -iname "*.jar" 2>/dev/null
/usr/share/java/postgresql-jdbc4.jar
/usr/share/java/java-atk-wrapper.jar
/usr/share/java/postgresql.jar
/usr/share/java/libintl-0.21.jar
/usr/share/java/postgresql-jdbc3.jar
/usr/share/java/postgresql-42.5.1.jar
/usr/share/java/libintl.jar
/usr/share/maven-repo/org/gnu/gettext/libintl/debian/libintl-debian.jar
/usr/share/maven-repo/org/gnu/gettext/libintl/0.21/libintl-0.21.jar
/usr/share/maven-repo/org/postgresql/postgresql/42.5.1/postgresql-42.5.1.jar
/usr/share/maven-repo/org/postgresql/postgresql/debian/postgresql-debian.jar
/usr/share/ca-certificates-java/ca-certificates-java.jar
/usr/lib/jvm/java-21-openjdk-riscv64/lib/jrt-fs.jar
/usr/lib/jvm/java-17-openjdk-riscv64/lib/jrt-fs.jar
user@starfive:~/Documents$

哎呀,Java 找不到 PostgreSQL JDBC 驱动程序 jar 文件。所以我们使用 java 命令的 -cp 选项。我们可以通过查看 find 命令的结果看到该软件包将 JDBC jar 文件安装在目录 /usr/share/java 中,因此我们使用通配符路径名,以便 Java 根据此 Stackoverflow 帖子找到它所需的任何内容,在 Java classpath 中包含目录中的所有 jar。如果我们使用 java -cp "/usr/share/java/*" HelloWorld.java,它就会工作,并且我们看到了 SQL SELECT 语句的结果。

user@starfive:~/Documents/java-projects$ java -cp "/usr/share/java/*" HelloWorld.java
Hello, World!
+1 123 456 7890  John Doe
+101 123 456 7890  Jane Doe
+1 987 456 1234  Ralph Doe
user@starfive:~/Documents/java-projects$

CLASSPATH 环境变量的使用尚有争议,但对于简单的 Java 应用程序,使用它可以减少输入,使一些事情变得更容易。我所做的就是在 /etc/environment 文件中添加一行,在登录时设置 CLASSPATH="/usr/share/java/*"。然后我注销并重新登录,并使用 env 命令进行检查。此后,我不再需要 java 命令的 -cp 选项。

Rust 安装

在 VisionFive 2 上安装 Rust 工具链相当简单。我使用了这篇文章的文档,如何在 Debian 11 上安装 Rust 编程语言,通过这些步骤安装并启用了稳定版的 Rust。

  • sudo apt install wget curl build-essential gcc make 确保这些应用程序可用
  • sudo wget -qO - https://sh.rustup.rs | sudo RUSTUP_HOME=/opt/rust CARGO_HOME=/opt/rust sh -s -- --no-modify-path -y 下载 Rust 安装程序
  • 修改 /etc/environment,添加所需的环变量,RUSTUP_HOME=/opt/rust
  • rustup default stable 下载并设置默认的 rust 工具链
  • sudo echo 'export PATH=$PATH:/opt/rust/bin' | sudo tee -a /etc/profile.d/rust.sh 修改 PATH 环境变量以包含 Rust 编译器

然后我使用以下命令设置我的当前终端会话

  • source /etc/environment 设置 RUSTUP_HOME 环境变量
  • PATH=$PATH:/opt/rust/bin 将 rust 编译器设置到 PATH 搜索路径中

然后我尝试了一个用 Rust 编程语言编写的简单 HelloWorld 程序,使用 vi 文本编辑器创建了一个文件 hello-world.rs,其内容如下,我用 rustc hello-world.rs 编译它以生成一个可执行文件 hello-world

fn main() {
    println!("Hello World, welcome to Rust.");
}

这行得通,现在我们来看一个更复杂的例子,使用 rust 显示我们示例数据库中的内容。

Rust 与 PostgreSQL

为了延续我们对编程语言进行简单 PostgreSQL 数据库查询的模式,让我们看一下 Rust 中的一个示例。我们很快发现 Rust 有一个相当复杂的底层基础设施,你需要理解这种视角并在这种世界观下工作。

首先是找一个例子,我从这个例子开始,使用 Postgres。我发现的一件事是,似乎大多数事情都是通过 cargo 命令后跟各种参数来完成的。请参阅为什么我在用 rustc 编译时会得到“找不到板条”的错误,尽管它被列为 Cargo.toml 中的依赖项?。所以即使在我们上面的 hello-world.rs 例子中我们直接用 rustc 编译,对于更复杂的东西来说,这并不是真正的方法。

首先,我们使用 cargo new rust-postgresql 创建我们的 Rust 项目。然后 cd 进入新创建的文件夹 rust-postgresql。您将看到一个子目录 src 和一个文件 Cargo.toml。在子目录 src 中有一个文件 main.rs,这是一个简单的 hello world 应用程序,我们将替换它。

接下来,我们需要使用 cargo add postgres 安装 PostgreSQL Rust crate。cargo 命令将设置各种内容,包括更新 Cargo.toml 文件并创建一个额外的文件 Cargo.lock

接下来,我们将替换由 cargo new 创建的占位符 main.rs 文件,在 src 子目录中放入我们的 main.rs 源文件,该文件从 PostgreSQL 数据库中读取数据。

use postgres::{Client, Error, NoTls};

fn main() -> Result<(), Error> {
    let mut client = Client::connect("postgresql://php:@localhost/dbtest", NoTls)?;

    for row in client.query("SELECT * FROM phonebook", &[])? {
        let phonenumber: &str = row.get(0);
        let firstname: &str = row.get(1);
        let lastname: &str = row.get(2);
        println!(
            " {} | {} | {}",
            phonenumber, firstname, lastname
        );
    }
    Ok(())
}

然后我们用 cargo build 编译我们的程序。可执行文件出现在我们项目文件夹的一个子目录中,与 src 子目录处于同一级别,即 ./target/debug

使用代码

以上编程语言示例都对 PostgreSQL 数据库表进行简单查询并打印结果。

PHP 与 PostgreSQL 的测试

测试 PHP 和 PostgreSQL 引擎是否相互通信的最简单方法是创建一个简单的 PHP 脚本,您可以从命令行运行该脚本来创建表,向表中插入一行,然后读回并打印它。

在运行此 PHP 脚本之前,您需要有一个有效的用户/角色和一个已创建的数据库。这两者都可以在尝试运行以下脚本之前使用 psql 工具创建。此 Stackoverflow 帖子描述了 PHP 的错误处理,现在所有错误都是异常,set_error_handler() 在 PHP7 中会发生什么?

#!/usr/bin/php
<?php

// Must install postgresql drivers for php.
//   sudo apt-get install php-pgsql

function exception_error_handler($errno, $errstr, $errfile, $errline ) {
    throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
}

set_error_handler("exception_error_handler");

try {
    $db = pg_connect("user=php dbname=dbtest");
    $result1 = pg_query($db, "CREATE TABLE phonebook(phone VARCHAR(32), firstname VARCHAR(32), lastname VARCHAR(32), address VARCHAR(64));");
    $result1 = pg_query($db, "INSERT INTO phonebook(phone, firstname, lastname, address) VALUES('+1 123 456 7890', 'John', 'Doe', 'North America')");
    $result1 = pg_query($db, "SELECT * FROM phonebook");
    while($data = pg_fetch_array($result1, NULL, PGSQL_ASSOC)) {
        print  $data["firstname"] . " " .  $data["lastname"] . "\n";
    }
} catch (Exception $e) {
        echo $e->getMessage();
}

?>

拥有上述文件后,您可以使用 chmod +x [文件名] 使其在 bash 命令行中可执行。此时,您可以运行 PHP 脚本并查看会发生什么。如果您想多次运行它,可以在 CREATE TABLE 语句之前添加一个额外的 pg_query 调用来执行 DROP TABLE phonebook; SQL 语句,例如 $result1 = pg_query($db, "DROP TABLE phonebook;");

如果用户/角色不正确,您将看到类似以下的错误

pg_connect(): Unable to connect to PostgreSQL server: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL:  Peer authentication failed for user "xuser"

或者数据库名称有问题,您会看到类似以下内容

pg_connect(): Unable to connect to PostgreSQL server: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL:  database "dbtestx" does not exist

或者返回结果有问题,您将看到类似以下内容

PHP Parse error:  syntax error, unexpected variable "$result1" in /home/user/Documents/github/web/phpdb/displaydb.php on line 28

Mosquitto 示例程序

为了编译 Mosquitto 客户端测试程序,您需要安装 GNU C 编译器。编译和链接示例的命令行必须指定 Mosquitto 通信、JSON 文本消息解析和持久数据存储所需的额外库

cc -o mqtttest mqtttest.c -lmosquitto -lfastjson -lsqlite3

该程序简短而简单,有四种不同的选项。如果您不带任何参数运行该程序,它将打印参数的简要摘要。

user@starfive:~/Documents/github/raspberrypi/mqtt$ ./mqtttest
mqtttest [-ps msgqueue] [-h hostname]
    -p msgqueue    -> publish messages to message queue msgqueue
    -s msgqueue    -> subscribe to message queue msgqueue
    -h hostname    -> publish or subscribe to broker on hostname
    -d             -> dump the SQLite database to standard output
user@starfive:~/Documents/github/raspberrypi/mqtt$

使用此示例程序最简单的方法是打开两个终端窗口,并在其中一个窗口中编译示例程序后,运行 ./mqtttest -s "topic/test" 来启动订阅者,然后在另一个窗口中运行 ./mqtttest -p "topic/test" 来启动发布者。然后您将看到订阅者打印出发送给主题的一系列接收到的消息,因为发布者发送了一系列消息。订阅者将每条消息记录到 SQLite3 数据库中。订阅者将继续等待新消息,直到在该终端窗口中用 control-C 杀死它。

可以使用 ./mqtttest -d 将订阅者创建的 SQLite3 数据库内容转储到标准输出。由于输出到标准输出,如果需要,可以将其重定向到文件。

兴趣点

第一点,从某种意义上说也是最根本的一点是,在探索 VisionFive 2 SBC 软件时,我感到多么自由地犯错。设备的尺寸以及取出 microSD 卡并放入新镜像重新开始的便利性,使实验变得更容易。

撰写本文最终成为一场旋风式的编程语言之旅。我一直想学习 Rust,所以这是一个至少浅尝辄止的机会。对于我这个 C/C++ 程序员来说,Rust 的语法很复杂。它感觉与 Go 或 Java 有些不同。然而,C++ 自 C++11 以来变得更加复杂,最新的标准更改导致其与 C++11 之前有了很大的不同。函数式编程正在渗透到越来越多最常见的编程语言中。

我惊讶于互联网搜索提供了多少过时、错误、不完整或无法使用的信息。而且我真的不喜欢为了找到我真正需要的东西而不得不浏览又一个 Docker 设置说明。

在撰写本文期间,我使用的软件在 VisionFive 2 上似乎相当可靠和健壮。起初,当我看到“不稳定”作为安装的软件包时,我担心会遇到问题。我想说,我遇到的 99% 的问题都是由于我自己的错误和无知造成的。我认为这实际上强调了软件的一个关键基石:在其他平台和架构上可靠、健壮地工作的软件,只要用于构建它们的工具链生成可靠、健壮的可执行文件,并且底层固件和操作系统堆栈可靠、健壮,通常在新架构上也能同样良好地工作。

历史

  • 2023 年 12 月 16 日:文章的初始版本
© . All rights reserved.