PipyJS - 函数式网络编程语言

InfoQ 2022-09-23 09:10:50 阅读数:964

编程网络函数语言pipyjs
这篇文章主要介绍 
Pipy
 以及强大的 PipyJS - 没有垃圾回收开销、轻量级的函数式 JavaScript 解释引擎。

Pipy 概览

Pipy 是一个
开源的
、轻量级、高性能、模块化、可编程的云原生网络流处理器,适用于边缘路由、负载均衡和代理解决方案、API 网关、静态 HTTP 服务器、服务网格边车、策略即代码等应用场景。
技术术语有点多,我们继续深入了解一下。

轻量级

Pipy 的可执行文件只有 10MB 左右,没有任何外部依赖,仅需很少的内存就可以运行。

高性能

Pipy 是使用 C++ 并基于异步 I/O 库 Asio 开发的。

模块化

Pipy 的核心使用了模块化设计,提供了大量可复用的小模块(过滤器)。这些过滤器可以组装成管道,网络数据流经这些管道并被处理。

流处理器

Pipy 将网络流数据字节抽象成事件,以事件驱动的方式来操作网络流。提供了事件驱动管道的抽象,管道使用输入流、执行用户定义的转换并输出流。

可编程

Pipy 对外屏蔽了底层的细节,提供了类似拼图的编程方式来实现业务目标。Pipy 是通过强大的 PipyJS 实现的,也是接下来我们要详细介绍的。

为什么使用 JavaScript

就像标题介绍的,PipyJS 是基于 JavaScript 的。Pipy 决定使用类似 JavaScript 脚本语言是有原因的:
  • JavaScript 可能是
    世界上最广泛使用的语言
  • 它的语法有 C 和 Java 的影子,因此对大多数程序员不陌生
  • Pipy 作为流处理器,意味着有大量的数据转换和重组的工作。通过 JavaScript 提供的很多强大的语法糖可以轻松搞定,比如
    展开语法
    解构赋值
    、以及数组的 
    map()
    reduce()
     等操作
  • JSON 作为网络上最广泛使用的数据格式,JavaScript 提供了原生支持可以轻松操作
  • 最后也是最最重要的,JavaScript 包含了“函数式编程”范式
Pipy 就像是一些彼此相连的管道一样。每个管道由一系列的
过滤器
组成,每个过滤器就像接收输入并返回特定结果的
函数
。对于这种设计模式,假如你的目标是保持连贯和简单,函数式编程语言是最适合 Pipy 的。

PipyJS 介绍

Pipy 通过自研的 PipyJS 组件来解析 JavaScript,该组件是 Pipy 代码库的一部分,但不依赖它。
PipyJS 是一个小且可嵌入的 JavaScript 引擎,专为高性能而设计,没有垃圾回收开销。它支持 ECMAScript 标准的子集,并且在某些方面进行了大胆的扩展。Pipy 拥抱
纯函数
式 JavaScript,在 PipyJS 中,
万物皆表达式

数据类型

与标准 JavaScript 一样, PipyJS 支持 4 种基本类型和结构数据的 
对象
 类型。
  • 基本类型Undefined:变量未初始化时的唯一值 
    undefinde
    。布尔类型 Boolean:
    true
     和 
    false
    。数字类型 Number:64 位双精度浮点数,如 
    123
    0xabc
    3.1415926
    1e6
    。字符串类型 String:Unicode 字符序列。
  • 结构化数据类型Null 对象:唯一值 
    null
    。用户定义的 POD(plain old data) ,如 
    { x: 1.2, y: 300 }
    内置对象:比如 
    Array
    RegExp
    。函数:比如 
    (a, b) => a * a + b * b

操作符

PipyJS 支持所有的标准 JavaScript 操作符,包括一些在 ES2020 才引入的,比如 
可选链操作符
 和 
空值合并运算符

全局变量

全局变量在 PipyJS 中也叫做 
上下文变量
。这里的
上下文
概念等同于高并发网络编程中的
连接
,每个连接都有彼此独立的
状态
。在 PipyJS 中,为方便起见这些隔离的状态保存在全局变量中,这也是为什么有时我们称其
上下文变量
。这也很好理解为什么 PipyJS 全局变量与标准 JavaScript 不同,简单说就是在不同的连接中值不相同。这点上,有点像
线程本地存储
全局变量通过内置的函数 
pipy()
 来定义,它通常是脚本中第一个调用的函数。
pipy({
 _myGlobalVariable: undefined
})
为了方便起见,全局变量使用下划线作为变量名的首字符,尽管不是语言强制的。
全局变量的作用范围是单个文件或者
模块
,多个模块间可通过 
export()
 和 
import()
 来共享。
// file A
pipy().export('namespace-1', {
 __myGlobalVariable: undefined
})

// file B
pipy().import({
 __myGlobalVariable: 'namespace-1'
})
为了方便起见,导出的全局变量名使用双下划线开头,但不强制。

本地变量

在 PipyJS 中,我们使用嵌套在局部变量函数范围内的函数参数作为本地变量。
void ((
 x, y, z, // declare local variables as function arguments
) => (
 x = 0,
 y = 0,
 z = 0 // initialize and use the variables in the function body
))() // Don't miss the () to invoke the function right away!
假如想要上面的表达式执行后返回结果,则需要删除开头的 
void

分支

在 PipyJS 中,万物皆表达式。不存在代码块或者流程控制,不能写 
if
 或者 
for
 语句。但这并不是说就不能写分支和循环了,而只是换了种形式:
函数式
简单分支可以使用逻辑运算符 
&&
res.status === 200 && (_result = 'OK', console.log('Success.'))

// That's equivalent to:
if (res.status === 200) {
 _result = 'OK';
 console.log('Success.');
}
注意:上面是 PipyJS 分支语法,下面是标准 JavaScript 语法(下同)。
多选择分支可以将逻辑运算符 
&&
 和 
||
 组合使用。
(res.status === 200) && (
 _result = 'OK'
) ||
(res.status === 404) && (
 _result = 'Not found'
) || (
 _result = ''
)

// That's equivalent to:
if (res.status === 200) {
 _result = 'OK';
} else if (res.status === 404) {
 _result = 'Not found';
} else {
 _result = '';
}

循环

可以使用 
Array.forEach()
 来实现简单有界的循环。
new Array(100).fill(0).forEach(
 (_, i) => (
 console.log(i)
 )
)

// That's equivalent to:
for (let i = 0; i < 100; i++) {
 console.log(i);
}
或者,对于通用条件循环,可以使用
内置的
函数&nbsp;
repeat()
void ((
 n, i
) => (
 n = i = 1,
 repeat(
 () => (
 n *= i,
 i += 1,
 i <= 10
 )
 )
))()

// That's equivalent to:
let n = 1, i = 1;
while (i <= 10) {
 n *= i;
 i += 1;
}

派生 ECMAScript

PipyJS 专为速度设计,其结构对于编写高性能网络流处理逻辑至关重要。下面重点介绍 PipyJS 与标准 ECMAScript 的不同或者未实现的:
  • 面向对象编程(OOP)结构 - 没有用户定义类型或者会构造器,没有
    原型
    系统
  • 控制流
  • 关键字
    break
    &nbsp;,&nbsp;
    case
    &nbsp;,&nbsp;
    catch
    &nbsp;,&nbsp;
    continue
    &nbsp;,&nbsp;
    debugger
    &nbsp;,&nbsp;
    default
    &nbsp;,&nbsp;
    do
    &nbsp;,&nbsp;
    else
    &nbsp;,&nbsp;
    finally
    &nbsp;,&nbsp;
    function
    &nbsp;,&nbsp;
    for
    &nbsp;,&nbsp;
    if
    &nbsp;,&nbsp;
    return
    &nbsp;,&nbsp;
    switch
    &nbsp;,&nbsp;
    throw
    &nbsp;,&nbsp;
    try
    &nbsp;,&nbsp;
    while
    &nbsp;,&nbsp;
    with
    &nbsp;,&nbsp;
    yield
    &nbsp;,&nbsp;
    class
    &nbsp;,&nbsp;
    import
    &nbsp;,&nbsp;
    export
    &nbsp;,&nbsp;
    extends
    &nbsp;,&nbsp;
    static
    &nbsp;,&nbsp;
    super
  • 类型系统
    BigInt
    &nbsp;and&nbsp;
    SymbolStrings
    &nbsp;内部存储为&nbsp;
    UTF-8
    ,在脚本中可以作为&nbsp;
    UTF-32
    &nbsp;来访问。比如&nbsp;
    &quot;&quot;.length
    &nbsp;在标准 JavaScript 中是 2,但在 PipyJS 中是 1
  • 变量 - 没有用来声明变量的关键词&nbsp;
    var
    &nbsp;或者&nbsp;
    let
尽管 PipyJS 派生自标准 ECMAScript,但是通过
函数式
机制来填补空白。

如何使用

现在你应该对 PipyJS 有了一定的了解,接下来我们看下如何编写代码并运行。
首先
下载
对应平台的二进制文件,将下面的代码保存为 ”hello.js“。
pipy()

.listen(8080)
 .serveHTTP(
 new Message('Hi, there!\n')
 )
然后使用刚刚下载的二进制文件执行该脚本。
pipy hello.js

2022-02-23 14:14:33.315 [INF] [config]
2022-02-23 14:14:33.316 [INF] [config] Module /hello.js
2022-02-23 14:14:33.316 [INF] [config] ================
2022-02-23 14:14:33.316 [INF] [config]
2022-02-23 14:14:33.316 [INF] [config] [Listen on 8080 at 0.0.0.0]
2022-02-23 14:14:33.316 [INF] [config] ----->|
2022-02-23 14:14:33.316 [INF] [config] |
2022-02-23 14:14:33.316 [INF] [config] serveHTTP
2022-02-23 14:14:33.316 [INF] [config] |
2022-02-23 14:14:33.316 [INF] [config] <-----|
2022-02-23 14:14:33.316 [INF] [config]
2022-02-23 14:14:33.316 [INF] [listener] Listening on port 8080 at 0.0.0.0
这段脚本会监听&nbsp;
8080
&nbsp;端口,并返回信息&nbsp;
Hi, there!
curl http://localhost:8080
Hi, there!

总结

Pipy
&nbsp;是有&nbsp;
Flomesh
&nbsp;开源的、极其快速且轻量级的网络流量处理器,适用于多种场景。Pipy 正由全职的提交者和贡献者积极地开发和维护中,尽管还还是早期的版本,但已经过多个商业客户生产环境的实战测试。它的创建者和维护者&nbsp;
Flomesh.cn
&nbsp;提供了以 Pipy 为核心的商业级解决方案。
这篇文章对 Pipy 进行了简短的概述和高阶的介绍。在&nbsp;
GitHub 页面
上可以找到教程和文档,也可以通过 Pipy 的管理控制台界面来访问。Pipy 社区非常欢迎贡献开发、尝试特定场景、或者提供简介和反馈。
版权声明:本文为[InfoQ]所创,转载请带上原文链接,感谢。 https://xie.infoq.cn/article/4adbeaed8973a2442b96e31bb