技术实践-Ballerina:云原生编程的 10 个引人注目的语言特征

关键要点

Ballerina的背景

集成可以被认为是一种编程类型,并且通常已经成为一种视觉表示,采用不同的技术试图简化和抽象复杂性。DSL 变得非常流行,因为它们为编程提供了正确的抽象,但它们也有局限性——通常情况下,集成开发人员必须使用常规代码来解决部分问题。此外,集成编程实践已经变得孤立,以至于使用所选集成工具进行编程的开发人员必须使用另一种工具或编程语言来开发其应用程序的其余部分。视觉表示对于能够观察端点之间的流动和交互仍然很重要。最重要的是,借助云原生工程,集成系统现在在容器中运行,

拥有一种能够提供与代码集成的能力以及可视化工具的语言不是非常有用吗?这样我们就可以解决 DSL 的局限性,并且能够保留软件工程的最佳实践?这种想法正是导致了芭蕾舞女演员项目的原因。目标是创建一种现代编程语言,将最好的编程语言、集成技术和云原生计算结合成一种针对集成进行优化并具有主流潜力的文本和图形语言。

让我们通过下面的列表来探索在Swan Lake Beta 版本中引入或增强的 Ballerina 语言的关键特性, 从而了解为什么 Ballerina 是创建网络服务和分布式应用程序集成的绝佳选择。

Ballerina的主要特征

1. Ballerina 以应用程序语言的健壮性和可扩展性解决脚本语言的大多数用例

您可以认为语言的范围从 Perl 或 Awk 等脚本语言到 Rust 或 C 等系统语言,中间有 Go 或 Java 等应用程序语言。程序可以用一行脚本编写,甚至可以编写数百万行代码。Ballerina 介于脚本语言和应用程序语言之间。

Ballerina 具有独特的功能,使其特别适合小型节目。大多数其他为较小程序设计的脚本语言与 Ballerina 的显着区别在于它们是动态类型的,并且它们不具有 Ballerina 所具有的独特的可伸缩性和健壮性功能。您可以使用其他脚本语言解决的前云时代的问题仍然是相关问题。除了现在,涉及网络服务;稳健性现在比以往任何时候都更加重要。使用标准脚本语言,一个 50 行的程序往往会在几年后变成一个不可维护的 1000 行程序,而且这不会扩展。Ballerina 可用于解决通过脚本语言程序解决的问题,但它更具可扩展性、更健壮且更适合云。

2. Ballerina 是面向数据而非面向对象的

在网络交互中,面向对象的方法将数据与代码捆绑在一起,这并不是通过广泛分布的微服务和 API 网络发送数据的最佳方式。

在前云时代,你的 API 是对路径中库的函数调用,你可以在调用中传递对象。但是当您的 API 在云中时,您不能这样做。您希望能够通过独立于代码的网络发送数据,因为您不想公开您的代码。尽管 JAVA RMI 和 CORBA 尝试支持分布式对象,但当您具有紧密耦合并且两端都属于同一方时,它就可以工作。分布式对象在松散耦合的云中不起作用。Ballerina 强调独立于用于处理数据的任何代码的纯数据。虽然 Ballerina 为内部接口提供对象,但它不是面向对象的语言。

随着云中的服务越来越多,开发人员被赋予了在其代码中使用网络资源的额外责任。编程语言本身必须有助于此操作。这就是为什么 Ballerina 配备了一个网络友好型系统,该系统具有强大的功能来处理网络上的数据。

JSON 是Ballerina中的“通用语” 。Ballerina 中的数据类型非常接近 JSON,并且一组基本的数据类型(数字、字符串、映射和数组)一对一映射到 JSON。Ballerina 的普通内存数据值几乎是内存中的 JSON。这允许来自网络的 JSON 有效负载立即进入语言并在没有转换或序列化的情况下对其进行操作。

import ballerina/io;
import ballerina/lang.value;

json j = { "x": 1, "y": 2 };

// Returns the string that represents j in JSON format.
string s = j.toJsonString();

// Parses a string in the JSON format and returns the value that 
// it represents.
json j2 = check value:fromJsonString(s);

// Allows null for JSON compatibility.
json j3 = null;

public function main() {
    io:println(s);
    io:println(j2);
}

3. Ballerina 有一个灵活的类型系统

编程语言的类型系统允许您描述您的位如何组合在一起,并且已经超越了捕获一类错误——现在这只是类型系统为您所做的一小部分。这也是提供良好 IDE 体验的重要部分。

脚本语言具有动态类型,而应用程序语言具有传统的静态类型,如 C++ 或 Java。如前所述,Ballerina 是一种脚本语言,但它也具有应用程序语言的特性,包括静态类型系统。在静态类型语言中,类型兼容性在编译时进行检查。静态类型语言通常对重构更健壮,更容易调试,并有助于创建更好的语言工具。

尽管 Ballerina 的类型系统是静态的,但它比应用程序语言中的类型灵活得多。Ballerina 语言的类型系统主要是结构化的,并增加了对名义类型的支持。这意味着类型兼容性是通过考虑值的结构来识别的,而不仅仅是依赖于类型的名称。这与具有标称类型系统的 Java、C++ 和 C# 等语言不同,其中类型兼容性受实际类型名称的约束。您可以根据需要对结构多说或少说。有了这个,我们通过接受在编译时不会捕获某些东西来获得简单性和灵活性。

详细地说,您可以说它类似于在 XML 模式中定义结构的方式。如果程序接收到的 XML 有效负载发生变化或偏差,它仍会处理它可以识别的内容。如果有效载荷中的某些更改无法识别,则不会导致失败。Ballerina 的类型系统既可以用作描述网络数据的模式语言,也可以用作处理内存中值的程序的类型系统。

在此处阅读有关 Ballerina 灵活类型系统的更多信息 。

4. Ballerina 具有处理网络数据的强大功能

Ballerina 还带有一组用于处理数据的语言功能,其中集成的查询功能脱颖而出。此功能允许您使用类似 SQL 的语法查询数据,如下所示。查询表达式包含一组类似于 SQL 的子句来处理数据。它们必须以 from 子句开头,并且可以执行过滤、连接、排序、限制和投影等各种操作。

import ballerina/io;
 
type Employee record {
    string firstName;
    string lastName;
    decimal salary;
};
 
public function main() {
    Employee[] employees = [
        {firstName: "Rachel", lastName: "Green", salary: 3000.00},
        {firstName: "Monica", lastName: "Geller", salary: 4000.00},
        {firstName: "Phoebe", lastName: "Buffay", salary: 2000.00},
        {firstName: "Ross", lastName: "Geller", salary: 6000.00},
        {firstName: "Chandler", lastName: "Bing", salary: 8000.00},
        {firstName: "Joey", lastName: "Tribbiani", salary: 10000.00}
    ];
    
  // Query-like expressions for list comprehensions start with from
  // and end with select.
  // The order by clause sorts members in employees based on the last name.
    Employee[] sorted = from var e in employees
                         order by e.lastName ascending 
                         select e;
    io:println(sorted);
}

还有一种 Table 数据类型,可以轻松处理关系数据和表格数据。下面的代码创建了一个包含 Employee 类型成员的表,其中每个成员都使用其名称字段进行唯一标识。主函数检索键值为 John 的 Employee 并为每个员工执行加薪。


import ballerina/io;

type Employee record {
    readonly string name;
    int salary;
};

// Creates a table with Employee type members, where each
// member is uniquely identified using their name field.
table key(name) t = table [
    { name: "John", salary: 100 },
    { name: "Jane", salary: 200 }
];

function increaseSalary(int n) {
    // Iterates over the rows of t in the specified order.
    foreach Employee e in t {
        e.salary += n;
    }
}

public function main() {
    // Retrieves Employee with key value `John`.
    Employee? e = t["John"];
    io:println(e);

    increaseSalary(100);
    io:println(t);
}

在此处阅读有关在 Ballerina 中编写集成查询的更多信息 。

此外,Ballerina 提供了内置的 XML 支持,其功能类似于 XQuery,并带有 XPath 等 XML 导航机制。这对于大量使用 XML 但不想使用特定于 XML 的语言的人特别有用,因为他们现在使用各种数据格式。

import ballerina/io;

public function main() returns error? {
    xml x1 = xml `Sherlock Holmes`;
    xml:Element x2 = 
        xml `
Sir Arthur Conan Doyle English
`; // `+` does concatenation. xml x3 = x1 + x2; io:println(x3); xml x4 = xml `Sherlock Holmes
Sir Arthur Conan Doyle English
`; // `==` does deep equals. boolean eq = x3 == x4; io:println(eq); // `foreach` iterates over each item. foreach var item in x4 { io:println(item); } // `x[i]` gives i-th item (empty sequence if none). io:println(x3[0]); // `x.id` accesses required attribute named `id`: // result is `error` if there is no such attribute // or if `x` is not a singleton. xml x5 = xml `Hello`; string id = check x5.id; io:println(id); // `x?.id` accesses optional attribute named `id`: // result is `()` if there is no such attribute. string? name = check x5?.name; io:println(name is ()); // Mutate an element using `e.setChildren(x)`. x2.setChildren(xml `French`); io:println(x2); io:println(x3); }

在无缝处理不同类型数据的其他功能中,Ballerina 中还有十进制数据类型。这些是为业务需求设计的浮点数,例如,用于指示价格。因为值在普通语言中以二进制表示,所以它们不能准确地表示所有实数。当数字多于格式允许的数字时,将省略剩余的数字——数字被四舍五入,从而产生精度错误。现实世界运行在十进制数字上,这就是为什么我们认为它是一种强大的能力。

import ballerina/io;

// The `decimal` type represents the set of 128-bits IEEE 754R 
// decimal floating point numbers.
decimal nanos = 1d/1000000000d;

function floatSurprise() {
    float f = 100.10 - 0.01;
    io:println(f);
}

public function main() {
    floatSurprise();
    io:println(nanos);
}

5. Ballerina 本质上是并发的,并为并发提供了内置的安全性

并发是云中的一项基本要求,因为您的网络操作具有很高的延迟。脚本语言通常不能很好地处理并发。像 Javascript 这样的脚本语言通常使用异步函数,这 比回调稍微好一点 (但不是很多)。使用 Ballerina,您拥有一个更简单的编程模型,它提供了异步函数的优点,但它是一种比异步函数更直接和直观的并发方法。

在 Ballerina 中,主要的并发概念是 strand,类似于 Go 中的 goroutine。Ballerina 程序在一个或多个线程上执行。一个线程可以与其他线程同时在单独的核心上运行,或者可以与其他线程在单个核心上抢先执行多任务。每个线程可以分为一个或多个线程,它们是语言管理的逻辑控制线程。从程序员的角度来看,strand 看起来确实像一个 OS 线程,但实际上并非如此——它更便宜、更轻量级。可以在单独的 OS 线程上调度 Strands。Go 遵循类似的方法,但没有多少编程语言这样做。事实上,大多数动态脚本语言不支持并发。例如,Python 有一个全局锁,因此它并不真正支持并行执行。

Ballerina 中的函数可以命名为“workers”,每个函数都与函数的默认工作者和其他命名工作者同时在新链上运行,如下所示:

import ballerina/io;

public function main() {
    // Code before any named workers is executed before named 
    // workers start.
    io:println("Initializing");
    final string greeting = "Hello";

  // Named workers run concurrently with the function's default       
  // worker and other named workers.
    worker A {
        // Variables declared before all named workers and 
        // function parameters are accessible in named workers.
        io:println(greeting + " from worker A");
    }

    worker B {
        io:println(greeting + " from worker B");
    }

    io:println(greeting + " from function worker");
}

Ballerina 还允许 strands 共享可变状态。通常,将并发和共享可变状态结合起来会产生数据竞争并给出不正确的结果。这是动态语言通常不公开线程的原因之一。然而,芭蕾舞女演员为这个问题提供了一个很好的解决方案,并通过协作多任务处理确保并发安全。属于同一线程的两条链不能同时运行。相反,Ballerina 合作(而不是抢先)将所有线程多任务处理到单个线程上,以避免锁定问题。这类似于异步函数,其中所有内容都在单个线程上运行,但没有复杂的编程模型。一条链通过让步实现协作式多任务处理。当一个链在特定的“屈服点”屈服时,运行时调度程序可以暂停该链的执行,

我们还可以确定何时可以并行运行 strand——注释可用于使 strand 在单独的线程上运行。这是因为 Ballerina 独特的类型系统可以确定服务何时锁定到足以安全地使用多个线程来并行处理传入请求。虽然这似乎无法提供大量的并行执行,但足以有效利用常见的云实例类型。

 import ballerina/io;

 public function main() {
// Each named worker has a "strand" (logical thread of  
// control) and execution and switches between strands only at   
// specific "yield" points.
    worker A {
        io:println("In worker A");
    }

    // An annotation can be used to make a strand run on a 
                // separate thread.
    @strand {
        thread: "any"
    }

    worker B {
        io:println("In worker B");
    }

    io:println("In function worker");
}

6. Ballerina有一个内在的图形视图

处理并发和网络交互是编写云程序的固有部分。在 Ballerina 中,每个程序都是一个自动说明分布式和并发交互的序列图。Ballerina 程序中的函数在文本语法和序列图中都有等效的表示。您可以在两个视图之间无缝切换。芭蕾舞女演员独特的图形视图并不是事后才想到的。事实上,它已被深入设计到语言中,以便提供关于函数的网络交互及其并发使用的真正洞察力。序列图是最适合这种情况的图表。

为了解释,Ballerina 的命名工作者(在第 5 点中讨论)和其他功能级并发特性描述了并发性,而客户端和服务的语言抽象描述了网络交互。垂直线也称为生命线,代表工作人员和远程端点。远程端点是一个客户端对象,它包含表示与远程系统的出站交互的远程方法。远程方法调用有不同的语法,大多数语言不区分远程调用和常规函数调用。水平线表示从函数的工作人员发送到另一个工作人员或从函数的工作人员发送到远程端点的消息。Ballerina 可以轻松区分这些关键方面,并向用户展示它们的高级视图,而无需用户执行任何操作。这仅是可能的,因为图形元素从一开始就被设计成语言。你没有得到这个用任何其他语言!

技术实践——Ballerina:云原生编程的 10 个引人注目的语言特征

Ballerina VSCode 插件可以从源代码动态生成序列图。要从上述 Ballerina 代码开始生成序列图,请 下载 VSCode 插件并启动图形查看器。

7. Ballerina 是云原生的,具有用于生产和消费服务以及将代码部署到云的简单模型

除了网络感知类型系统,Ballerina 还提供了用于处理网络服务的基本语法抽象。该语言还包括使用 Docker 和 Kubernetes 在云上部署 Ballerina 应用程序的内置支持。

服务对象产生服务

Ballerina 容纳了服务的概念,一个服务可以只用三四行 Ballerina 代码编写。Ballerina 中的服务由三个协同工作的事物提供支持:应用程序、侦听器和库。应用程序定义服务对象并将它们附加到侦听器。监听器由图书馆提供;例如,每个协议(HTTP/GraphQL 等)都有一个侦听器,由库提供。侦听器接收网络输入,然后调用应用程序以查找服务对象。服务对象支持两种接口样式:

由于 Ballerina 的服务方法与其独特的面向线路的类型系统相结合,您可以从 Ballerina 代码生成接口描述。这可以是 OpenAPI 或 GraphQL 规范。因此,您实际上可以编写常规的芭蕾舞演员服务对象并生成您的客户端代码。这些功能的组合使云集成能够顺利工作。

import ballerina/http;
 
service on new http:Listener(9090) {
  resource function get greeting(string name) returns string { 
    return "Hello, " + name; 
  } 
}

使用远程服务的客户端对象

出站网络交互由客户端对象表示。客户端具有代表与远程系统的出站交互的远程方法。客户端对象是允许我们绘制序列图的语法元素之一。

import ballerina/email;

function main() returns error? {
  email:SmtpClient sc
    = check new("smtp.example.com",
                "user123@example.com",
                "passwd123");
  check sc -> sendMessage({
     to: "contact@ballerina.io",
     subject: "Ballerina"
     body: "Ballerina is pretty awesome!"
  });
}
技术实践——Ballerina:云原生编程的 10 个引人注目的语言特征

代码到云

Ballerina 支持从代码生成 Docker 和 Kubernetes 工件,无需任何额外配置。这简化了在云中开发和部署 Ballerina 代码的体验。代码到云通过从代码中派生所需的值来构建容器和所需的工件。有关更多详细信息,请参阅此 示例 。

要将您的代码部署到不同的云平台,例如 AWS 和 Microsoft Azure,请使用服务对象上的注释来实现轻松的云部署,如下面的代码片段所示。Ballerina 编译器可以生成工件,例如 Dockerfile、Docker 映像、Kubernetes YAML 文件和无服务器函数。

例如,可以通过使用 @azure_functions:Function 注释 Ballerina 函数在 Azure 中部署 Ballerina 函数。

import ballerina/uuid;
import ballerinax/azure_functions as af;

// HTTP request/response with no authentication
@af:Function
public function hello(@af:HTTPTrigger { authLevel: "anonymous" } string payload) returns @af:HTTPOutput string|error {

    return "Hello, " + payload + "!";

}

8. 错误的显式控制流

处理错误的方法对语言设计和使用有着广泛的影响。它影响着语言的一切。当您处理网络时,错误是做生意的正常部分,尤其是考虑 到分布式计算的八个谬误时。许多前云语言,例如 Java、Javascript 和 Typescript,都使用异常作为处理错误的一种方式。但并非每种语言都遵循这种设计。Go 和 Rust 等语言甚至根本没有异常。

除了例外,控制流是隐式的,代码更难理解和维护。当出现问题时,只是方便地抛出异常会使一切变得完全混乱。为了使错误处理工作,您必须能够查看程序并了解是否存在错误,错误可能发生在哪里以及控制流将如何变化。因此,现在有一个相当强烈的趋势是消除异常并回到更简单的方法,其中错误是显式的,并在错误时使用正常的控制流。这种方法可以在 Go、Rust 和 Swift 中找到。Ballerina 遵循相同的方法,并允许开发人员使用带有显式错误控制流的错误数据类型。

import ballerina/io;

// Converts bytes to a string and then to an int.
function intFromBytes(byte[] bytes) returns int|error {

    string|error ret = string:fromBytes(bytes);

    // The is operator can be used to distinguish errors
    // from other values.
    if ret is error {

        return ret;
    } else {
        return int:fromString(ret);
    }
}

// The main function can return an error.
public function main() returns error? {

    int|error res = intFromBytes([104, 101, 108, 108, 111]);    
    if res is error {
        // The `check` expression is shorthand for this pattern of
        // checking if a value is an error and returning that value.
        return res;

    } else {
        io:println("result: ", res);
    }
}

9. 事务作为语言特征

编写使用事务的 Ballerina 程序非常简单,因为事务是一种语言特性。Ballerina 提供的不是事务性内存,而是对分隔事务的基本语言支持。这样,您始终可以保证您的事务具有开始、回滚或提交选项。

Ballerina 程序的运行实例包括事务管理器。这可以在与 Ballerina 程序相同的进程中运行,也可以在单独的进程中运行(不应通过不可靠的网络连接)。事务管理器维护从每个链到事务堆栈(或者,在分布式上下文中,事务分支)的映射。当一个链的事务栈非空时,我们说它处于事务模式;链的事务堆栈上最顶层的事务是该链的当前事务。

import ballerina/io;

public function main() returns error? {
    // Compile-time guarantees that transactions are bracketed with
    // begin and commit or rollback. Transaction statement begins
    // a new transaction and executes a block.
    transaction {
        doStage1();
        doStage2();

        // Committing a transaction must be done explicitly using a commit   
        // statement and it may cause an error.
        check commit;

    }
}

function doStage1() {
    io:println("Stage1 completed");
}

function doStage2() {
    io:println("Stage2 completed");
}

Ballerina 中的事务还包括其网络交互功能,即客户端和服务,以支持分布式事务。用户可以通过将服务的资源/远程方法和客户端对象的远程方法声明为事务性来创建客户端和服务之间的事务流。

10. 许多功能都很熟悉

语言的激增确实表明个人愿意学习新的语言。但企业似乎不愿意采用一种新语言,因为他们担心自己无法雇用熟悉该语言的人。重要的是要强调,虽然 Ballerina 提供了更好的做事方式,但它还附带了 C 系列语言程序员熟悉的部分功能,足以让开发人员在几个小时或更短的时间内开始使用。

流行的 C 系列语言(C、C++、Java、JavaScript、C#、TypeScript)有很多共同点,而 Ballerina 通过以同样的方式做很多事情来利用这一点。如果您是一名对任何 C 系列语言都有一定编程经验的程序员,那么使用 Ballerina 进行编码将相当简单。除了强大的语言功能外,Ballerina这意味着该语言带有丰富的 标准库 (包含用于网络数据、消息和通信协议的库)、 包管理系统、结构化文档、测试框架,以及流行 IDE(尤其是 Visual Studio Code)的扩展/插件以及其他支持该语言的工具。

结论

虽然 Ballerina 具有现代编程语言的所有通用功能,但它的优势在于它独特地提供了语言功能,使其更易于使用、组合和创建云网络服务。开发人员现在可以构建弹性、安全和高性能的服务,解决分布式计算的谬误,并将它们集成以创建云原生应用程序,只需使用专门的编程语言即可。

展开阅读全文

页面更新:2024-04-30

标签:语言   线程   引人注目   函数   特征   事务   类型   代码   功能   数据   系统   技术   网络

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top