写点什么

PipyJS - 函数式网络编程语言

作者:Flomesh
  • 2022 年 9 月 23 日
    北京
  • 本文字数:3451 字

    阅读完需:约 11 分钟

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 位双精度浮点数,如 1230xabc3.14159261e6。字符串类型 String:Unicode 字符序列。

  • 结构化数据类型 Null 对象:唯一值 null。用户定义的 POD(plain old data) ,如 { x: 1.2, y: 300 }内置对象:比如 ArrayRegExp。函数:比如 (a, b) => a * a + b * b

操作符

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

全局变量

全局变量在 PipyJS 中也叫做 上下文变量。这里的上下文概念等同于高并发网络编程中的连接,每个连接都有彼此独立的状态。在 PipyJS 中,为方便起见这些隔离的状态保存在全局变量中,这也是为什么有时我们称其上下文变量。这也很好理解为什么 PipyJS 全局变量与标准 JavaScript 不同,简单说就是在不同的连接中值不相同。这点上,有点像线程本地存储

全局变量通过内置的函数 pipy() 来定义,它通常是脚本中第一个调用的函数。

pipy({  _myGlobalVariable: undefined})
复制代码

为了方便起见,全局变量使用下划线作为变量名的首字符,尽管不是语言强制的。

全局变量的作用范围是单个文件或者模块,多个模块间可通过 export() 和 import() 来共享。

// file Apipy().export('namespace-1', {  __myGlobalVariable: undefined})
// file Bpipy().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);}
复制代码

或者,对于通用条件循环,可以使用内置的函数 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 , case , catch , continue , debugger , default , do , else , finally , function , for , if , return , switch , throw , try , while , with , yield , class , import , export , extends , static , super

  • 类型系统 BigInt and SymbolStrings 内部存储为 UTF-8,在脚本中可以作为 UTF-32 来访问。比如 "😀".length 在标准 JavaScript 中是 2,但在 PipyJS 中是 1

  • 变量 - 没有用来声明变量的关键词 var 或者 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.js2022-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] serveHTTP2022-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
复制代码

这段脚本会监听 8080 端口,并返回信息 Hi, there!

curl http://localhost:8080Hi, there!
复制代码

总结

Pipy 是有 Flomesh 开源的、极其快速且轻量级的网络流量处理器,适用于多种场景。Pipy 正由全职的提交者和贡献者积极地开发和维护中,尽管还还是早期的版本,但已经过多个商业客户生产环境的实战测试。它的创建者和维护者 Flomesh.cn 提供了以 Pipy 为核心的商业级解决方案。

这篇文章对 Pipy 进行了简短的概述和高阶的介绍。在 GitHub 页面上可以找到教程和文档,也可以通过 Pipy 的管理控制台界面来访问。Pipy 社区非常欢迎贡献开发、尝试特定场景、或者提供简介和反馈。

发布于: 刚刚阅读数: 3
用户头像

Flomesh

关注

微信订阅号:flomesh 2022.04.07 加入

一站式云原生应用流量管理供应商 官网:https://flomesh.io

评论

发布
暂无评论
PipyJS - 函数式网络编程语言_Service Mesh 服务网格_Flomesh_InfoQ写作社区