分类 软件开发 下的文章

这些方便的 Go 构建选项可以帮助你更好地理解 Go 的编译过程。

学习一门新的编程语言最令人欣慰的部分之一,就是最终运行了一个可执行文件,并获得预期的输出。当我开始学习 Go 这门编程语言时,我先是阅读一些示例程序来熟悉语法,然后是尝试写一些小的测试程序。随着时间的推移,这种方法帮助我熟悉了编译和构建程序的过程。

Go 的构建选项提供了更好地控制构建过程的方法。它们还可以提供额外的信息,帮助把这个过程分成更小的部分。在这篇文章中,我将演示我所使用的一些选项。注意:我使用的“ 构建 build ”和“ 编译 compile ”这两个词是同一个意思。

开始使用 Go

我使用的 Go 版本是 1.16.7。但是,这里给出的命令应该也能在最新的版本上运行。如果你没有安装 Go,你可以从 Go 官网 上下载它,并按照说明进行安装。你可以通过打开一个命令提示符,并键入下面的命令来验证你所安装的版本:

$ go version

你应该会得到类似下面这样的输出,具体取决于你安装的版本:

go version go1.16.7 linux/amd64

基本的 Go 程序的编译和执行方法

我将从一个在屏幕上简单打印 “Hello World” 的 Go 程序示例开始,就像下面这样:

$ cat hello.go
package main

import "fmt"

func main() {
    fmt.Println("Hello World")
}

在讨论更高级的选项之前,我将解释如何编译这个 Go 示例程序。我使用了 build 命令,后面跟着 Go 程序的源文件名,本例中是 hello.go,就像下面这样:

$ go build hello.go

如果一切工作正常,你应该看到在你的当前目录下创建了一个名为 hello 的可执行文件。你可以通过使用 file 命令验证它是 ELF 二进制可执行格式(在 Linux 平台上)。你也可以直接执行它,你会看到它输出 “Hello World”。

$ ls
hello  hello.go

$ file ./hello
./hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

$ ./hello
Hello World

Go 提供了一个方便的 run 命令,以便你只是想看看程序是否能正常工作,并获得预期的输出,而不想生成一个最终的二进制文件。请记住,即使你在当前目录中没有看到可执行文件,Go 仍然会在某个地方编译并生成可执行文件并运行它,然后把它从系统中删除。我将在本文后面的章节中解释。

$ go run hello.go
Hello World

$ ls
hello.go

更多细节

上面的命令就像一阵风一样,一下子就运行完了我的程序。然而,如果你想知道 Go 在编译这些程序的过程中做了什么,Go 提供了一个 -x 选项,它可以打印出 Go 为产生这个可执行文件所做的一切。

简单看一下你就会发现,Go 在 /tmp 内创建了一个临时工作目录,并生成了可执行文件,然后把它移到了 Go 源程序所在的当前目录。

$ go build -x hello.go

WORK=/tmp/go-build1944767317
mkdir -p $WORK/b001/

<< snip >>

mkdir -p $WORK/b001/exe/
cd .
/usr/lib/golang/pkg/tool/linux_amd64/link -o $WORK \
/b001/exe/a.out -importcfg $WORK/b001 \
/importcfg.link -buildmode=exe -buildid=K26hEYzgDkqJjx2Hf-wz/\
nDueg0kBjIygx25rYwbK/W-eJaGIOdPEWgwC6o546 \
/K26hEYzgDkqJjx2Hf-wz -extld=gcc /root/.cache/go-build /cc \
/cc72cb2f4fbb61229885fc434995964a7a4d6e10692a23cc0ada6707c5d3435b-d
/usr/lib/golang/pkg/tool/linux_amd64/buildid -w $WORK \
/b001/exe/a.out # internal
mv $WORK/b001/exe/a.out hello
rm -r $WORK/b001/

这有助于解决在程序运行后却在当前目录下没有生成可执行文件的谜团。使用 -x 显示可执行文件确实在 /tmp 工作目录下创建并被执行了。然而,与 build 命令不同的是,可执行文件并没有移动到当前目录,这使得看起来没有可执行文件被创建。

$ go run -x hello.go


mkdir -p $WORK/b001/exe/
cd .
/usr/lib/golang/pkg/tool/linux_amd64/link -o $WORK/b001 \
/exe/hello -importcfg $WORK/b001/importcfg.link -s -w -buildmode=exe -buildid=hK3wnAP20DapUDeuvAAS/E_TzkbzwXz6tM5dEC8Mx \
/7HYBzuaDGVdaZwSMEWAa/hK3wnAP20DapUDeuvAAS -extld=gcc \
/root/.cache/go-build/75/ \
7531fcf5e48444eed677bfc5cda1276a52b73c62ebac3aa99da3c4094fa57dc3-d
$WORK/b001/exe/hello
Hello World

模仿编译而不产生可执行文件

假设你不想编译程序并产生一个实际的二进制文件,但你确实想看到这个过程中的所有步骤。你可以通过使用 -n 这个构建选项来做到这一点,该选项会打印出通常的执行步骤,而不会实际创建二进制文件。

$ go build -n hello.go

保存临时目录

很多工作都发生在 /tmp 工作目录中,一旦可执行文件被创建和运行,它就会被删除。但是如果你想看看哪些文件是在编译过程中创建的呢?Go 提供了一个 -work 选项,它可以在编译程序时使用。-work 选项除了运行程序外,还打印了工作目录的路径,但它并不会在这之后删除工作目录,所以你可以切换到该目录,检查在编译过程中创建的所有文件。

$ go run -work hello.go
WORK=/tmp/go-build3209320645
Hello World

$ find /tmp/go-build3209320645
/tmp/go-build3209320645
/tmp/go-build3209320645/b001
/tmp/go-build3209320645/b001/importcfg.link
/tmp/go-build3209320645/b001/exe
/tmp/go-build3209320645/b001/exe/hello

$ /tmp/go-build3209320645/b001/exe/hello
Hello World

其他编译选项

如果说,你想手动编译程序,而不是使用 Go 的 buildrun 这两个方便的命令,最后得到一个可以直接由你的操作系统(这里指 Linux)运行的可执行文件。那么,你该怎么做呢?这个过程可以分为两部分:编译和链接。你可以使用 tool 选项来看看它是如何工作的。

首先,使用 tool compile 命令产生结果的 ar 归档文件,它包含了 .o 中间文件。接下来,对这个 hello.o 文件执行 tool link 命令,产生最终的可执行文件,然后你就可以运行它了。

$ go tool compile hello.go

$ file hello.o
hello.o: current ar archive

$ ar t hello.o
__.PKGDEF
_go_.o

$ go tool link -o hello hello.o

$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

$ ./hello
Hello World

如果你想进一步查看基于 hello.o 文件产生可执行文件的链接过程,你可以使用 -v 选项,它会搜索每个 Go 可执行文件中包含的 runtime.a 文件。

$ go tool link -v -o hello hello.o
HEADER = -H5 -T0x401000 -R0x1000
searching for runtime.a in /usr/lib/golang/pkg/linux_amd64/runtime.a
82052 symbols, 18774 reachable
        1 package symbols, 1106 hashed symbols, 77185 non-package symbols, 3760 external symbols
81968 liveness data

交叉编译选项

现在我已经解释了 Go 程序的编译过程,接下来,我将演示 Go 如何通过在实际的 build 命令之前提供 GOOSGOARCH 这两个环境变量,来允许你构建针对不同硬件架构和操作系统的可执行文件。

这有什么用呢?举个例子,你会发现为 ARM(arch64)架构制作的可执行文件不能在英特尔(x86\_64)架构上运行,而且会产生一个 Exec 格式错误。

下面的这些选项使得生成跨平台的二进制文件变得小菜一碟:

$ GOOS=linux GOARCH=arm64 go build hello.go

$ file ./hello
./hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, not stripped

$ ./hello
bash: ./hello: cannot execute binary file: Exec format error

$ uname -m
x86_64

你可以阅读我之前的博文,以更多了解我在 使用 Go 进行交叉编译 方面的经验。

查看底层汇编指令

源代码并不会直接转换为可执行文件,尽管它生成了一种中间汇编格式,然后最终被组装为可执行文件。在 Go 中,这被映射为一种中间汇编格式,而不是底层硬件汇编指令。

要查看这个中间汇编格式,请在使用 build 命令时,提供 -gcflags 选项,后面跟着 -S。这个命令将会显示使用到的汇编指令:

$ go build -gcflags="-S" hello.go
# command-line-arguments
"".main STEXT size=138 args=0x0 locals=0x58 funcid=0x0
        0x0000 00000 (/test/hello.go:5) TEXT    "".main(SB), ABIInternal, $88-0
        0x0000 00000 (/test/hello.go:5) MOVQ    (TLS), CX
        0x0009 00009 (/test/hello.go:5) CMPQ    SP, 16(CX)
        0x000d 00013 (/test/hello.go:5) PCDATA  $0, $-2
        0x000d 00013 (/test/hello.go:5) JLS     128

<< snip >>

你也可以使用 objdump -s 选项,来查看已经编译好的可执行程序的汇编指令,就像下面这样:

$ ls
hello  hello.go

$ go tool objdump -s main.main hello
TEXT main.main(SB) /test/hello.go
  hello.go:5            0x4975a0                64488b0c25f8ffffff      MOVQ FS:0xfffffff8, CX                  
  hello.go:5            0x4975a9                483b6110                CMPQ 0x10(CX), SP                       
  hello.go:5            0x4975ad                7671                    JBE 0x497620                            
  hello.go:5            0x4975af                4883ec58                SUBQ $0x58, SP                          
  hello.go:6            0x4975d8                4889442448              MOVQ AX, 0x48(SP)                       

<< snip >>

分离二进制文件以减少其大小

Go 的二进制文件通常比较大。例如, 一个简单的 “Hello World” 程序将会产生一个 1.9M 大小的二进制文件。

$ go build hello.go
$
$ du -sh hello
1.9M    hello
$
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
$

为了减少生成的二进制文件的大小,你可以分离执行过程中不需要的信息。使用 -ldflags-s -w 选项可以使生成的二进制文件略微变小为 1.3M。

$ go build -ldflags="-s -w" hello.go
$
$ du -sh hello
1.3M    hello
$
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
$

总结

我希望这篇文章向你介绍了一些方便的 Go 编译选项,同时帮助了你更好地理解 Go 编译过程。关于构建过程的其他信息和其他有趣的选项,请参考 Go 命令帮助:

$ go help build

题图由 Ashraf ChembanPixabay 上发布。


via: https://opensource.com/article/22/4/go-build-options

作者:Gaurav Kamathe 选题:lkxed 译者:lkxed 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

即使初学者也可以,并且应该采取这些步骤来保护他们的 WordPress 网站免受网络攻击。

 title=

WordPress 已经驱动了互联网 30% 的网站,它是世界上增长最快的 内容管理系统 content management system (CMS),而且不难看出原因:通过大量可用的定制化代码和插件、一流的 搜索引擎优化 Search Engine Optimization (SEO),以及在博客界超高的美誉度,WordPress 赢得了很高的知名度。

然而,随着知名度而来的,也带来一些不太好的关注。WordPress 是入侵者、恶意软件和网络攻击的常见目标,事实上,在 2019 年被黑客攻击的 CMS 中,WordPress 约占 90%

无论你是 WordPress 新用户或者有经验的开发者,这里有一些你可以采取的重要步骤来保护你的 WordPress 网站。以下 6 个关键技巧将帮助你起步。

1、选择可靠的托管主机

主机是所有网站无形的基础,没有它,你不能在线发布你的网站。但是主机的作用远不止起简单的托管你的网站,它也要对你的网站速度、性能和安全负责。

第一件要做的事情就是检查主机在它的套餐中是否包含 SSL 安全协议。

无论你是运行一个小博客或是一个大的在线商店,SSL 协议都是所有网站必需的安全功能。如果你正在进行线上交易,你还需要 高级 SSL 数字证书 ,但是对大多数网站来说,基本免费的 SSL 证书就很好了。

其他需要注意安全功能包括以下几种:

  • 日常的自动离线网站备份
  • 恶意软件和杀毒软件扫描和删除
  • 分布式服务攻击 Distributed denial of service (DDOS)保护
  • 实时网络监控
  • 高级防火墙保护

另外除了这些数字安全功能之外,你的主机供应商的 物理 安全措施也是值得考虑的。这些包括用安全警卫、闭路监控和二次验证或生物识别来限制对数据中心的访问。

2、使用安全插件

保护你的网站安全最有效且容易的方法之一是安装一个安全插件,比如 Sucuri,它是一个 GPLv2 许可的开源软件。安全插件是非常重要的,因为它们能将安全管理自动化,这意味着你能够集中精力运行你的网站,而不是花大量的时间来与在线威胁作斗争。

这些插件探测、阻止恶意攻击,并提醒你需要注意的任何问题。简言之,它们持续在后台运行,保护你的网站,这意味着你不必保持 7 天 24 小时地保持清醒,与黑客、漏洞和其他数字垃圾斗争。

一个好的安全插件会免费提供给你所有必要的安全功能,但是一些高级功能需要付费订阅。举个例子,如果你想要解锁 Sucuri 的网站防火墙 ,你就需要付费。开启 网站应用防火墙 web application firewall (WAF)阻挡常见的威胁,并为给你的网站添加一个额外的安全层,所以当选择安全插件的时候,寻找带有这个功能的插件是一个好的主意。

3、选择值得信任的插件和主题

WordPress 的快乐在于它是开源的,所以任何人、每个人都能提供他们开发的主题和插件。但当选择高质量的主题和插件时,这也抛出一些问题。

在挑选免费的主题或插件时,有一些设计较差,或者更糟糕的是,可能会隐藏恶意代码。

为了避免这种情况,始终从可靠的来源来获取免费主题和插件,比如 WordPress 主题库。阅读对它的评论,并研究查看开发者是否构建过其他的程序。

过时的或设计不良的主题和插件可以为攻击者进入你的网站留下“后门”或错误,这就是为什么选择时要谨慎。然而,你也应该提防无效或者破解的主题。这些已经黑客破坏了的高级主题被非法销售。你可能会购买一个无效的主题,它看起来没什么问题,但会通过隐藏的恶意代码破坏你的网站。

为了避免无效主题,不要被打折的价格所吸引,始终坚持可靠的主题商店,比如官方的 WordPress 目录。如果你在其它地方寻找,坚持选择大型且值得信任的商店,比如 Themify ,这个主题和插件商店自从 2010 年就已经在经营了。Themify 确保它的所有 WordPress 主题通过了 谷歌友好移动 Google Mobile-Friendly 测试,并在 GNU 通用公共许可证 下开源。

4、运行定期更新

这是 WordPress 的基本规则: 始终保持你的网站最新。然而,不是所有人都坚持了这个规则,只有 43% 的 WordPress 网站 运行的是最新版本。

问题是,当你的网站过期的时候,由于它在安全和性能修复方面落后的原因,容易受到故障、漏洞、入侵和崩溃的影响。过期的网站不能像更新的网站一样修复漏洞,攻击者能够分辨出哪些网站是过期的。这意味着他们能够依此来搜索最易受攻击的网站并袭击它们。

这就是为什么你始终要运行最新的 WordPress 版本的原因。为了保持网站安全处于最强的状态,你必须更新你的插件和主题,以及你的核心 WordPress 软件。

如果你选择一个受管理的 WordPress 托管套餐,你可能会发现你的供应商会为你检查并运行更新,以了解你的主机是否提供了软件和插件更新。如果没有,你可以安装一个开源插件管理器。比如 GPLv2 许可的 Easy Updates Manager plugin 作为替代品。

5、强化你的登录

除了通过仔细选择主题和安装安全插件来创建一个安全的 WordPress 网站外,你还需要防止未经授权的登录访问。

密码保护

如果你在使用 容易猜到的短语 比如 “123456” 或 “qwerty” ,第一步要做的增强登录安全最简单的方法是更改你的密码。

尝试使用一个长的密码而不是一个单词,这样它们很难被破解。最好的方式是用一系列你容易记住且不相关的单词合并起来。

这里有一些其它的提示:

  • 绝不要重复使用密码
  • 密码不要包括像家庭成员的名字或者你喜欢的球队等明显的单词
  • 不要和任何人分享你的登录信息
  • 你的密码要包括大小写和数字来增加复杂程度
  • 不要在任何地方写下或者存储你的登录信息
  • 使用 密码管理器

变更你的登录地址

将默认登录网址从标准格式 yourdomain.com/wp-admin 变更是一个好主意。这是因为黑客也知道这个缺省登录网址,所以不变更它会有被暴力破解的风险。

为避免这种情况,可以将登录网址变更为不同的网址。使用开源插件比如 GPLv2 许可的 WPS Hide Login 可以更加安全、快速和轻松的自定义登录地址。

应用双因素认证

为了提供更多的保护,阻止未授权的登录和暴力破解,你应该添加双因素认证。这意味着即使有人 确实 得到了你的登录信息,但是他们还需要一个直接发送到你的手机上的验证码,来获得对你的 WordPress 网站管理的权限。

添加双因素认证是非常容易的,只需要安装另一个插件,在 WordPress 插件目录搜索 “two-factor authentication” ,然后选择你要的插件。其中一个选择是 Two Factor ,这是一个流行的 GPLv2 许可的插件,已经有超过 10000 次安装。

限制登录尝试

WordPress 可以让你多次猜测登录信息来帮助你登录。然而,这对黑客尝试获取未授权访问 WordPress 网站并发布恶意代码也是有帮助的。

为了应对暴力破解,安装一个插件来限制登录尝试,并设置你允许猜测的次数。

6、禁用文件编辑功能

这不是一个适合初学者的步骤,除非你是个自信的程序员,不要尝试它。并且一定要先备份你的网站。

那就是说,如果你真的想保护你的 WordPress 网站,禁用文件编辑功能 一个重要的措施 。如果你不隐藏你的文件,它意味着任何人从管理后台都可以编辑你的主题和插件代码,如果入侵者进入,那就危险了。

为了拒绝未授权的访问,转到你的 .htaccess 文件并输入:

<Files wp-config.php>
order allow,deny
deny from all
</Files>

或者,要从你的 WordPress 管理后台直接删除主题和插件的编辑选项,可以添加编辑你的 wp-config.php 文件:

define( 'DISALLOW_FILE_EDIT', true );

保存并重新加载这个文件,插件和主题编辑器将会从你的 WordPress 管理后台菜单中消失,阻止任何人编辑你的主题或者插件代码,包括你自己。如果你需要恢复访问你的主题和插件代码,只需要删除你添加在 wp-config.php 文件中的代码即可。

无论你阻止未授权的访问,还是完全禁用文件编辑功能,采取行动保护你网站代码是很重要的。否则,不受欢迎的访问者编辑你的文件并添加新代码是很容易的。这意味着攻击者可以使用编辑器从你的 WordPress 站点来获取数据,或者甚至利用你的网站对其他站点发起攻击。

隐藏文件更容易的方式是利用安全插件来为你服务,比如 Sucuri 。

WordPress 安全概要

WordPress 是一个优秀的开源平台,初学者和开发者都应该享受它,而不用担心成为攻击的受害者。遗憾的是,这些威胁不会很快消失,所以保持网站的安全至关重要。

利用以上措施,你可以创建一个更加健壮、更安全的保护水平的 WordPress 站点,并给自己带来更好的使用体验。

保持安全是一个持续的任务,而不是一次性的检查清单,所以一定要定期重温这些步骤,并在建立和使用你的CMS时保持警惕。


via: https://opensource.com/article/20/4/wordpress-security

作者:Lucy Carney 选题:lujun9972 译者:hwlife 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Gitbase 是一个由 Go 驱动的开源项目,它使得我们可以在 Git 仓库上运行 SQL 查询。

Git 已经成为了代码版本控制的事实标准。虽然 Git 已经很流行了,但想用它来对源代码仓库的历史和内容进行深度分析,仍然是一件复杂的事情。

另一方面,SQL 则是一个经过实际检验、适合查询大型代码库的的语言,毕竟 Spark 和 BigQuery 等项目都采用了 SQL 作为查询语言。

因此,在 source{d} 公司,我们顺理成章地结合了这两种技术来创建了 Gitbase:这是一个用 SQL 对 Git 仓库进行大规模分析的“代码即数据”解决方案。

Gitbase 是一个完全开源的项目,它站在一系列巨人的肩膀上,是它们使 Gitbase 的发展成为可能。本文旨在指出其中的主要部分。

Gitbase 试验场 提供了一种使用 Gitbase 的可视化方式。

使用 Vitess 解析 SQL

Gitbase 将 SQL 作为用户接口。这意味着我们需要解析基于 MySQL 协议传输的 SQL 请求,并理解它们。幸运的是,我们在 YouTube 的朋友和他们的 Vitess 项目已经实现了这一点。Vitess 是一个数据库集群系统,用于 MySQL 的水平扩展。

我们直接截取一些重要的代码片段,并把它做成了一个 开源项目。这个项目允许任何人在几分钟内编写一个 MySQL 服务器(正如我在 justforfunc 的专题:CSVQL - 用 SQL 处理 CSV 中所展示的那样)。

用 go-git 读取 Git 储存库

当成功解析了一个请求,我们还需要读取数据集里的 Git 仓库,才能够知道该如何回复它。为此,我们集成了 source{d} 最成功的仓库 go-git。go-git 是一个高度可扩展的纯 Go 语言的 Git 实现。

这使得我们能够轻松地分析以 siva 文件格式存储在磁盘上的源代码仓库(siva 也是一个 source{d} 的开源项目),或是直接使用 git clone 克隆的仓库。

使用 Enry 检测编程语言,使用 Babelfish 解析文件

Gitbase 并没有将其分析能力局限于 Git 历史记录上。它还使用(显然也是)我们的开源项目 Enry 集成了语言检测功能,并使用 Babelfish 实现了程序解析的功能。Babelfish 是一个用于通用源代码解析的自托管服务器,它可以将代码文件转化为 通用抽象语法树 Universal Abstract Syntax Trees (UAST)。

这两个功能在 Gitbase 中呈现为用户函数 LANGUAGEUAST。结合使用两个函数,许多查询请求都成为了可能,比如“找到上个月修改次数最多的函数名称”。

让它快速运行

Gitbase 经常要分析非常大的数据集,比如公共 Git 档案,其中有来自 GitHub 的 3TB 源代码(见 公告)。为了做到这一点,每份 CPU 处理能力都很重要。

这就是为什么我们又集成了另外两个项目:Rubex 和 Pilosa。

使用 Rubex 和 Oniguruma 加快正则表达式的速度

Rubex 是 Go 的 regexp 标准库包的一个准替代品。之所以还不能完成替代,是因为他们没有在 regexp.Regexp 类型上实现 LiteralPrefix 方法,不过我也是直到现在才听说这个方法。

Rubex 的高性能得归功于高度优化的 C 语言库 Oniguruma,它使用 cgo 来调用这个库。

使用 Pilosa 索引加快查询速度

索引基本上是每个关系型数据库的众所周知的特性,但 Vitess 却没有实现索引,因为它不是真正需要。

还好开源的 Pilosa 再一次拯救了我们,它是一个用 Go 实现的分布式位图索引,使得 Gitbase 可以用于大规模的数据集。Pilosa 是开源的,它极大地加快了对多个海量数据集的查询。

总结

我想通过这篇博文,亲自感谢开源社区,是他们让我们在如此短的时间内创建了 Gitbase,这是谁也没想到的。在 source{d} 公司,我们是开源的坚定信仰者,github.com/src-d 下的每一行代码(包括我们的 OKR 和投资者委员会)都可以证明这一点。

你想尝试一下 Gitbase 吗?最快、最简单的方法就是使用 source{d} 引擎。从 sourced.tech/engine 下载它,只需一个命令就能让 Gitbase 运行起来。

想了解更多吗?请查看我在 Go SF meetup 的演讲录音。

这篇文章 最初发表在 Medium 上,经授权后在此重新发布。


via: https://opensource.com/article/18/11/gitbase

作者:Francesc Campoy 选题:lkxed 译者:lkxed 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

你可能会遇到各种各样的工具来帮助你快速构建企业的应用。

然而,大多数值得信赖的选择往往是专有产品。因此,你将被锁定在他们的平台上,而对于你利用什么来构建的应用,没有足够的了解。

开源的解决方案应该是一个完美的替代品,让你安心,并对你的关键业务应用充满信心。

Budibase 就是这样一个令人印象深刻的解决方案。

Budibase:开源的低代码平台让事情变得简单

Budibase 是一个越来越受欢迎的开源低代码平台,可以帮助你为企业建立应用。

你可以从头开始创建应用,或者使用现有的模板来快速建立表单、机构-客户门户、汽车租赁管理面板、会计师门户、职位申请追踪器等等。

虽然它确实使事情变得简单,而不需要你有必要的编程技巧,但它也提供了一些控制,让你在一定程度上定制应用。

它支持一系列的数据源:MySQL、Rest API、OracleDB、MongoDB、Google 表格等。

你可以选择自我托管并在你的服务器上部署应用,或者利用 Budibase 的云托管服务。

Budibase 的特点

Budibase 提供了大部分的基本功能。让我在这里强调一下重要的功能:

  • 支持外部数据源,包括 MongoDB、MySQL 等。
  • 支持 Rest API 拉取数据。
  • 能够使用应用的内置数据库或上传 CSV 来导入数据。
  • 各种数据类型和功能,包括附件、关系、公式等。
  • API 整合平台,整合不同的 API,帮助你轻松建立内部应用、表单等。
  • 能够使用内部表格生成自动页面。
  • 构建单页应用。
  • 自动生成的 CRUD(创建、读取、更新和删除)页面
  • 私人和公共应用。
  • 只需点击几下就可以定制你的应用的主题。
  • 容易为你的应用实现深色模式主题。
  • 一个功能丰富的表单生成器,满足广泛的要求。
  • 支持 Webhook。
  • 与诸如 Zapier 等的第三方集成。
  • 基于特定触发器的灵活自动化选项。
  • 能够将 JavaScript 添加到你的自动程序中。
  • 为拥有自己基础设施的用户提供自我托管选项。
  • 免费的单点登录认证/管理。
  • 用户管理选项,将团队分配到不同的应用。
  • 支持 SMTP 电子邮件。
  • 电子邮件模板,以配合你的品牌和风格。
  • 支持 OAuth 登录。目前仅限于谷歌。
  • 图表、表格和卡片来优雅地展示数据。

总的来说,当你登录到该服务并查看其产品时,还有很多东西可以探索。

在我短暂的使用中,我发现用户界面很舒适,很容易使用。为了给你更多的认识,我在下面分享了更多关于使用 Budibase 的信息。

使用 Budibase 快速建立一个应用

在使用此类服务时,用户体验是首要因素。

Budibase 在这方面没有让你失望。当你开始使用 Budibase 时,你会得到一个很好的用户体验。

就我使用过的开源平台而言,用户体验并不总是强项。但是,在这种情况下,是用户体验使这个工具易于使用。

你可以迅速开始建立一个应用程序,添加你的源,并在几次点击中开始设计。

你可以选择内部数据库或选择外部源。该平台让你根据需要编辑/创建/导入数据。

而且,只需点击几下(取决于你应用的规模),你就可以开始设计屏幕和调整布局。

它完全是一个可视化编辑器,所以你会得到你所看到的东西。在布局中添加容器、分区、表单、卡片、图表和许多其他元素。

调整主题是一件轻而易举的事。因此,你可以根据你的要求,匹配你的品牌风格/样式,或者根据你的要求进行创意。

你可以轻松地编辑数据,创建新的数据字段,也可以为数据启用搜索索引。

不要忘了,你还可以获得所有的自动化选项,与其他服务集成,使用 Webhook、cron 任务或应用动作来响应触发器。这些是为你的用户建立一个最有效的应用的一些最重要的东西。

下面是我使用 Budibase 建立一个样本应用跟踪系统时的情况:

当然,你可以选择在你的服务器上发布应用,或者使用 Budibase 的云服务。

总结

Budibase 是一个非常有用的低代码平台,应该可以帮助个人和企业快速建立各种应用。你应该在其官方网站和 GitHub 页面上探索更多关于它的信息。

Budibase

它消除了聘请专家为各种用例构建应用的需要。而且,作为一个你可以自行托管的开源平台,它可以让你扩展并提供对构建应用的完全控制,而无需支付额外费用。

如果需要,你还可以选择其企业产品,提供高级支持和专门定制的服务。


via: https://itsfoss.com/budibase/

作者:Ankush Das 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

CyberDB,一个基于 Python 字典和列表的内存数据库。

概括

CyberDB 是一个轻量级的 Python 内存数据库。它旨在利用 Python 内置数据结构字典、列表作数据存储,通过 TCP 套接字高效通信,并提供了数据持久化。该数据库的亮点在于它使用了 Pythonic 的方式编程,你可以像使用字典和列表一样使用 CyberDB。

现在我们把 CyberDB 带到能发挥其作用的地方,在生产环境中将 CyberDB 作为 Flask 的内存数据库,使用 Gunicorn 运行,并实现多进程间的通信。

这篇文章通过一个尽可能精简的 Flask 实例讲解,不会涉及复杂的 Web 知识。核心思路为 CyberDB + Gunicorn + Gevent + Flask(多进程 + 协程),启动一个 CyberDB 服务器,使用 Gunicorn 多进程运行 Flask 实例,每个进程的实例通过 Gevent 运行,进程中使用 CyberDB 客户端连接至内存数据库,由此实现对 CyberDB 数据库的高并发访问。

源码解析

文章使用 PyPy 运行,同样适用 CPython。

运行环境: Debian 10, Python 3.8.12, PyPy 7.3.7

此项目的目录结构

.
├── app.py
├── cyberdb_init.py
├── cyberdb_serve.py
├── requirements.txt
└── venv

我们通过列举每个文件的内容顺序讲解 CyberDB 的核心操作。

文件 requirements.txt

CyberDB>=0.7.1
Flask==2.1.1
gevent==21.12.0
gunicorn==20.1.0

这是此项目的依赖。这篇文章不是 Python 基础教程,如果你不清楚,请查询相关文档创建虚拟环境 venv 目录并安装 requirements.txt 中的依赖。

生成 venv 目录并安装好依赖后,下面所有操作都在激活的虚拟环境中运行。

文件 cyberdb\_init.py

功能:初始化 CyberDB 的表结构,只在第一次运行时使用,后续不再使用。

import time

import cyberdb

db = cyberdb.Server()
# 配置 CyberDB 服务端的 地址、端口、密码。
db.start(host='127.0.0.1', port=9980, password='123456')

# 待服务端启动后,连接 CyberDB 服务端。
time.sleep(3)
client = cyberdb.connect(host='127.0.0.1', port=9980, password='123456')
# 生成 proxy 对象。
with client.get_proxy() as proxy:
    # 创建类型为 CyberDict 的表 centre,并初始化内容。
    proxy.create_cyberdict('centre')
    centre = proxy.get_cyberdict('centre')
    centre['content'] = 'Hello CyberDB!'

# 将 CyberDB 保存至 data.cdb 。
db.save_db('data.cdb')

在项目根目录执行

python cyberdb_init.py

以完成 CyberDB 数据库表的初始化。

它会在 CyberDB 中创建了一个名为 centre、类型为 CyberDict 的表;初始化 content 键的值为 Hello CyberDB!;最后将 CyberDB 数据库保存至硬盘(在项目根目录生成了名为 data.cdb 的文件)。

文件 cyberdb\_serve.py

功能:运行 CyberDB 服务端。

import cyberdb

def main():
    # 后台运行 CyberDB 服务端,设置相关信息。
    db = cyberdb.Server()
    # 从硬盘读取 data.cdb 至 CyberDB。
    db.load_db('data.cdb')
    # 每 300 秒备份一次数据库。
    db.set_backup('data.cdb', cycle=300)
    db.run(
        host='127.0.0.1', # TCP 运行地址
        port=9980, # TCP 监听端口
        password='hWjYvVdqRC', # 数据库连接密码
        max_con=10000, # 最大并发数
        encrypt=True, # 加密通信
        print_log=False # 不打印日志
    )


if __name__ == '__main__':
    main()

在项目根目录执行

python cyberdb_serve.py

以运行 CyberDB 服务端。

此处设置了 encrypt=True ,CyberDB 会将 TCP 通信内容使用 AES-256 算法加密。开启 encrypt=True 后,CyberDB 仅允许白名单中的 IP 通信,默认白名单为 ['127.0.0.1'](查看白名单 设置方法)。一般,若只需在本地进程间通信,无需开启 encrypt=True 和设置白名单,只有远程通信时需要此操作。

文件 app.py

功能:运行 Flask 实例和 CyberDB 客户端。

import cyberdb
from flask import Flask, g

# 连接 CyberDB 并生成客户端实例。
client = cyberdb.connect(
    host='127.0.0.1', 
    port=9980, 
    password='hWjYvVdqRC',
    # 服务端若加密,客户端必须加密,反之亦然。
    encrypt=True,
    # 每个连接若超过900秒无操作,将舍弃该连接。
    # 连接由连接池智能管理,无需关注细节。
    time_out=900
)

# 创建 Flask 实例,此部分请参考 
# Flask 文档 https://flask.palletsprojects.com/
app = Flask(__name__)

@app.before_request
def before_request():
    # 每次请求执行前生成 proxy 对象。
    g.proxy = client.get_proxy()
    # 从连接池获取连接。
    g.proxy.connect()

@app.get("/")
def hello_world():
    # 从数据库获取 centre 表。
    centre = g.proxy.get_cyberdict('centre')
    
    return {
        'code': 1,
        'content': centre['content']
    }

@app.teardown_request
def teardown_request(error):
    # 每次请求执行后归还连接至连接池。
    g.proxy.close()

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=8000)

该模块会在每次请求执行前(before_request())使用 client.get_proxy() 获取 proxy 对象,每个获取的 proxy 对象可以绑定一个 TCP 连接,此处使用 proxy.connect() 从连接池获取连接。视图函数 hello_world() 中,由 proxy 获取的对象 centre,与 proxy 共用同一个连接,proxy 的连接释放后,centre 也会失去连接。在每次请求后(teardown_request())使用 proxy.close() 方法释放 proxy 绑定的连接,归还至连接池。

cyberdb.connecttime_out 参数表示连接池中每个连接的超时时间,此处每个连接超过 900 秒无操作将被舍弃。若不设置该参数,连接池的每个连接会维持到失效为止。

使用 Gunicorn 运行 Flask 实例

Gunicorn 是一个用于 UNIX 的 Python WSGI HTTP 服务器,通常在生产环境使用,可以利用多核 CPU 。

Gevent 是一个基于协程的 Python 网络库。Gevent 会更改 CyberDB 客户端的底层套接字通信,使之支持协程。

在项目根目录运行

gunicorn -w 4 -b 127.0.0.1:8000 -k gevent app:app

使用 4 进程、Gevent 启动 Flask 实例。

浏览器访问 127.0.0.1:8000 ,得到如下响应:

{"code":1,"content":"Hello CyberDB!"}

参考信息

CyberDB 源码: https://github.com/Cyberbolt/CyberDB

总结

通过此例,你可以把 CyberDB 部署到更复杂的 Web 环境中,充分享受内存的低延迟特性。CyberDB 的核心是以 Pythonic 的方式编程,你可以在任何 Python 代码中将 CyberDB 作为内存数据库。


作者简介:

Cyberbolt:一个自由的 Python 开发者。


via: https://www.cyberlight.xyz/static/cyberdb-chn/tutorial/flask/

作者:Cyberbolt 编辑:wxy

本文由贡献者投稿至 Linux 中国公开投稿计划,采用 CC-BY-SA 协议 发布,Linux中国 荣誉推出

首先在 Java 中创建初始化一个整数列表,然后在 Groovy 中做同样的事。

 title=

我非常喜欢 Groovy 编程语言。我喜欢它是因为我喜欢 Java,尽管 Java 有时候感觉很笨拙。正因为我是那么喜欢 Java,其他运行在 JVM 上语言都不能吸引我。比方说 Kotlin、Scala 还有 Clojure 语言,它们感觉上就和 Java 不一样,因为它们对于什么是好的编程语言的理解不同。Groovy 和它们都不一样,在我看来,Groovy 是一个完美的选项,特别是对于一部分程序员来说,他们喜欢 Java,但是又需要一个更灵活、更紧凑,并且有时候更直接的语言。

列表 List 这种数据结构是一个很好的例子,它可以容纳一个无序的列表,列表中的元素可以是数字、字符串或者对象,程序员可以用某种方式高效地遍历这些元素,特别是对于编写和维护脚本的人来说,“高效”的关键就是要有简洁清晰的表达,而不需要一大堆“仪式”,把代码的意图都变模糊了。

安装 Java 和 Groovy

Groovy 是基于 Java 的,因此需要同时安装一个 Java 才行。你的 Linux 发行版的仓库中可能有最近的比较好的 Java 版本。或者,你也可以在根据 这些指导 来安装 Groovy。对于 Linux 用户来说,SDKMan 是一个不错的代替选项,你可以使用它来获取多个 Java 和 Groovy 版本,以及许多其他的相关工具。在这篇文章中,我使用的 SDK 发行版是:

  • Java: OpenJDK 11 的 11.0.12-open 版本
  • Groovy: 3.0.8 版本

言归正传

Java 中有很多方法可以实例化并初始化列表,从它最初被引入的时候就有了(我记得是在 Java 1.5 的时候,但请不要引用我的话)。在这些方法里,有两个有趣的方法,它们涉及到了 java.util.Arraysjava.util.List 这两个类。

使用 java.util.Arrays 类

java.util.Arrays 类定义了一个 asList() 静态方法,它可以被用来创建一个基于数组的列表,因此大小是不可变的,尽管其中的元素是可以被修改的。下面是它的使用方式:

var a1 = Arrays.asList(1,2,3,4,5,6,7,8,9,10); // immutable list of mutable elements

System.out.println("a1 = " + a1);
System.out.println("a1 is an instance of " + a1.getClass());

// output is
// a1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// a1 is an instance of class java.util.Arrays$ArrayList

a1.set(0,0); // succeeds
System.out.println("a1 = " + a1); // output is
// a1 = [0, 2, 3, 4, 5, 6, 7, 8, 9, 10]

a1.add(11); // fails producing
// Exception in thread "main" java.lang.UnsupportedOperationException
System.out.println("a1 = " + a1); // not reached

使用 java.util.List 类

java.util.List 类定义了一个 of() 静态方法,它可以被用来创建一个不可变的列表,其中的元素是否可变要取决于它们本身是否支持修改。下面是它的使用方式:

var a2 = List.of(1,2,3,4,5,6,7,8,9,10);

System.out.println("a2 = " + a2);
System.out.println("a2 is an instance of " + a2.getClass());

// output is
// a2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// a2 is an instance of class java.util.ImmutableCollections$ListN

a2.set(0,0); // fails producing
// Exception in thread "main" java.lang.UnsupportedOperationException
System.out.println("a2 = " + a2); // not reached

a2.add(11); // also fails for same reason if above two lines commented out
System.out.println("a2 = " + a2); // not reached

因此,我可以使用 Arrays.asList(),也可以使用 List.of() 方法,前提是如果我想要的是一个大小不能改变、且不关心元素是否可变的列表。

如果我想要初始化一个可变的列表,我更倾向于把这些不可变的列表作为参数传给一个列表构造器,就像下面这样:

var a1 = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,7,8,9,10));

System.out.println("a1 = " + a1);
System.out.println("a1 is an instance of " + a1.getClass());

// output is
// a1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// a1 is an instance of class java.util.ArrayList

a1.set(0,0);
System.out.println("a1 = " + a1);

//output is
// a1 = [0, 2, 3, 4, 5, 6, 7, 8, 9, 10]

a1.add(11);
System.out.println("a1 = " + a1);

// output is
// a1 = [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

注意,这个 Arrays.asList() 方法是用来初始化这个新的 ArrayList<Integer>() 的,也就是说,它为这个传进来的列表创建了一个可变的拷贝。

现在,或许只有我这么想,但是这种方式确实看起来需要理解很多关于 java.util.Arraysjava.util.List 类的细节才行,而我只是想要创建并初始化一个数字列表而已(尽管真正使用到的语句并没有太多“仪式”)。下面是真正用到的那行代码,仅供参考:

var a1 = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,7,8,9,10));

Groovy 是怎么做的

下面来看看在 Groovy 中如何实现上述需求:

def a1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

println "a1 = $a1"
println "a1 is an instance of ${a1.getClass()}"

// output is
// a1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// a1 is an instance of class java.util.ArrayList

a1[0] = 0
println "a1 = $a1"

// output is
// a1 = [0, 2, 3, 4, 5, 6, 7, 8, 9, 10]

a1 << 11
println "a1 = $a1"

// output is
// a1 = [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

我们一眼就能发现,Groovy 使用了 def 关键字而不是 var 关键字。我还发现了,仅仅是把一系列的类型(在这个例子里是整数)放进括号里,我就得到了一个创建好的列表。此外,这样创建出来的列表完全就是我想要的:一个可变的 ArrayList 实例。

现在,或许再一次只有我这么想,但是上面的代码看起来要简单多得多 —— 不用记住 .of().asList() 返回的是“ 半不变 semi-mutable ”的结果,也不用为它们做一些补偿。另外一个好处是,我现在可以使用括号和下标来引用列表中的某个特定元素,而不用这个叫 set() 方法。另外,这个跟在列表后面的 << 操作符也很方便,我再也不用调用 add() 方法来添加元素啦。还有,你注意到代码中没有分号了吗?没错,在 Groovy 里,句末的分号并不是必须的。最后,我们来看看字符串插值,只要在字符串里用 $变量 或者 ${表达式} 就可以实现了哦!

在 Groovy 世界中还藏着许多“有待发掘”的东西。上面的列表定义其实是一个动态类型(Groovy 中默认)和 Java 中的静态类型的对比。在上面的 Groovy 代码定义的那一行,变量 a1 的类型是在运行的时候,根据等号右边的表达式的计算结果推断出来的。现在我们都知道,动态语言可以给我们带来强大的功能,有了强大的功能,我们有了很多机会去尝试不同的东西。对于那些不喜欢动态类型的程序员来说,Groovy 也支持静态类型。

Groovy 相关资源

Apache Groovy 网站上有非常多的文档。另一个很棒的 Groovy 资源是 Mr. Haki。学习 Groovy 还有一个很棒的原因,那就是可以接着学习 Grails,后者是一个优秀的、高效率的全栈 Web 框架,基于许多优秀组件构建而成,比如有 Hibernate、Spring Boot 和 Micronaut 等。


via: https://opensource.com/article/22/1/creating-lists-groovy-java

作者:Chris Hermansen 选题:lujun9972 译者:lkxed 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出