跳转到主要内容
Chinese, Simplified

关键的要点

  • 现代编程世界分为两种,一种是从事低级代码的受过高度训练的专业人员,另一种是没有深厚背景但专注于帮助满足业务需求的高级应用程序开发的人员。
  • Ballerina是一种开源编程语言,它通过提供创建云本地应用程序所需的抽象、工具和平台,专注于提高后者的工作效率。
  • 通过将网络抽象(如客户端对象、服务、资源函数和侦听器)作为语言类型系统的一部分,Ballerina对网络进行了不同的处理。因此开发人员可以使用语言提供的类型来编写正常工作的网络程序。
  • Ballerina的网络和数据友好型系统使开发人员能够以直观的方式建模网络通信模式,从而实现最大的生产力。
  • 内置的云支持通过读取源代码中定义的注释,帮助生成相应的部署工件。这些工件可以是dockerfile、Docker映像、Kubernetes YAML文件或无服务器函数。
  • Ballerina对并发和网络交互的抽象和语法进行了设计,因此与序列图有密切的对应关系。

今年早些时候,Mike Loukides, O 'Reilly Media内容策略的副总裁,发表了一篇关于“重新思考编程”的文章。它探讨了编程世界是如何在受过高度训练的专业人员和那些没有深厚背景但有丰富经验的人之间分化的。

前者涉及捆绑工具、框架和平台,而后者则专注于通过将不同的系统集成在一起来为其业务创建应用程序。

几乎所有的通用编程语言都使用抽象和构造来设计,以供受过高度训练的专业人员解决一些低级的编程问题。

Ballerina是一种开源编程语言,它引入了新的和基本的抽象来帮助那些想要非常敏捷的流程并使用云平台来根据业务需求扩展其应用程序的专业人员。

语言中的网络

几十年来,编程语言把网络简单地当作I/O源。因此,要公开简单的api,开发人员必须通过编写一个显式循环来实现这些服务,该循环等待网络请求,直到获得信号。通过将网络抽象(如客户机对象、服务、资源函数和侦听器)作为语言类型系统的一部分,Ballerina对网络进行了不同的处理,这样您就可以使用语言提供的类型来编写正常工作的网络程序。

使用Ballerina中的服务类型和侦听器对象,开发人员只需在资源函数中编写api主导的业务逻辑,就可以公开他们的api。根据侦听器对象中定义的协议,这些服务可以公开为HTTP/HTTPS、HTTP2、gRPC和WebSockets。Ballerina服务带有内置的并发性。对资源方法的每个请求都在单独的strand (Ballerina并发单元)中处理,它为服务提供了隐式的并发行为。

下面的代码块演示了Ballerina中的语法服务类型。

import ballerina/http;
service serviceName on new http:Listener(8080) {
   resource function newResource(http:Caller caller, http:Request request) {
       // API-led logic
   }
}

在请求-响应范例中,网络通信是通过阻塞调用来完成的,但是阻塞一个线程到一个网络调用是非常昂贵的。许多语言框架都支持异步调用,但是开发人员必须通过使用基于回调的代码技术来实现异步/等待。下面的Ballerina代码片段显示了一个简单的HTTP GET操作,对于开发人员来说,这似乎是一个阻塞操作。但是,在内部它使用非阻塞I/O执行异步执行,其中当前执行线程被释放到操作系统供其他操作系统使用。

http:Client clientEndpoint = new("http://example.com");

public function main() {
   var response = clientEndpoint->get("/get?test=123");
   -----     
}

由于不可靠的网络行为,api的远程调用很容易失败。有时自动重试将有助于从此类故障中恢复。在某些情况下,故障转移技术将有助于不间断地提供服务,或者像断路器这样的技术有助于防止跨多个程序的灾难性级联故障。将这些技术作为Ballerina客户端对象的一部分,可以帮助开发人员编写具有弹性和健壮性的远程网络调用代码。

下面的代码片段展示了如何在Ballerina HTTP客户端对象中配置断路器来处理与网络相关的错误。

http:Client backendClientEP = new("http://example.com", {
       circuitBreaker: {
           rollingWindow: {
               timeWindowInMillis: 10000,
               bucketSizeInMillis: 2000,
               requestVolumeThreshold: 0
           },
           failureThreshold: 0.2,
           resetTimeInMillis: 10000,
           statusCodes: [400, 404, 500]
       },
       timeoutInMillis: 2000
   });

网络服务必须处理各种用户输入数据。通常,如果没有正确地检查,所有用户输入都可能是危险的。污染分析旨在通过防止任何可以被用户输入修改的变量来提高安全性。Ballerina的内置污染分析器通过观察污染数据如何在程序中传播,帮助识别不受信任的(污染的)数据。如果将不受信任的数据传递给安全性敏感的参数,则会生成编译器错误。由于污染检查发生在编译器阶段,因此程序员可以重新设计程序,在危险的输入周围竖起一道安全的墙。

网络和数据友好型系统

Ballerina支持结构化类型系统,并处理称为形状的值的抽象。形状基本上忽略了值的存储标识。这意味着当将一个值与其他值进行比较时,它不会考虑该值的名称引用。这在组合来自多个独立设计系统的数据时特别有用。

Ballerina还支持联合类型,其中值集是其组件类型的值空间的联合。例如,您可以使用union类型的变量来存储字符串或int,但是在任何给定时间都只有一种类型的值。

Ballerina具有内置的网络数据类型支持,比如JSON和XML。Ballerina json类型是为处理json格式的数据表达式而设计的。它是union的内置名称,定义如下:

type json = () | boolean | int | float | decimal | string | json[] | map<json>

默认情况下,Ballerina有打开的记录。让我们看一下用来表示一个人的详细信息的记录类型。

type Gender "male"|"female";

type Address record {|
   string city;
   string country;
|};

type Person record {
   string name;
   int birthYear; 
   Gender gender;
   Address address?;
};

在这里,类型Person是一个开放的记录类型,使用“{”和“}”分隔符用包含记录类型描述符定义。通过在定义中使用“{|”和“|}”分隔符,地址类型是具有排他记录类型描述符的封闭记录类型。个人记录中的地址还有后缀“?”,使其成为可选字段,无需设置值即可跳过。

让我们创建一个新的类型Student。

type Student record {
   string name;
   int birthYear; 
   Gender gender;
   Address address?;
   string college;
};

Student类型是Person类型的子类型。与Person类型相比,它有一个额外的字段college类型字符串。这是可能的,因为Person类型是开放类型,其形状也可以有称为college的字符串字段。

public function main() {

   Student s1 = { name: "John", birthYear: 1995, gender: "male",
                     college: "US Berkeley" };
   Student s2 = { name: "Kamala", birthYear: 1994, gender: "female",
                 address: { city: "Milpitas", state: "CA" ,country: "USA"}, 
college: "Stanford" };
   Person p1 = s1;
   Person p2 = s2;

   io:println("P1's details:" ,p1);
   io:println("P2's details:" ,p2);

}

$ ballerina run person.bal
Compiling source
    person.bal
Running executables

P1's details:name=John birthYear=1995 gender=male college=US Berkeley
P2's details:name=Kamala birthYear=1994 gender=female address=city=Milpitas state=CA country=USA college=Stanford

语言集成查询是一种允许对多个数据源使用单一语法的特性。它将有助于把一个复杂的问题分解成一系列简短的、易于理解的查询。

Ballerina查询表达式使用类似sql的语法提供了一种语言集成的查询特性。与SQL语句不同,由于类型安全,查询表达式有助于在设计期间识别错误。

import ballerina/io;

type Student record {
   string name;
   int age;
   string school;
};

type Result record {
   string name;
   string college;
   float gpa;
   string school;
};

public function main() {
   map<float> gpa = {"John": 4.1, "Bill": 3.9, "Sam": 3.3, "Jennifer": 3.1};
   Student s1 = {name: "John", age: 18, school: "Milpitas High School"};
   Student s2 = {name: "Bill", age: 17, school: "San Jose High School"};
   Student s3 = {name: "Sam", age: 18, school: "Clara Coutinho High School"};
   Student s4 = {name: "Jennifer", age: 18, school: "Fremont Union High School"};
   Student[] student = [];
   student.push(s1);
   student.push(s2);
   student.push(s3);
   student.push(s4);

   Result[] stanford = from var candidate in student
                       let float cgpa = (gpa[candidate.name] ?: 0),
                       string targetCollege = "Stanford"
                       where cgpa > 3.8
                           select {
                               name: candidate.name,
                               college: targetCollege,
                               gpa: cgpa,
                               school: candidate.school
                           };   

   io:println(stanford);
}

$ ballerina run query_expression.bal
Compiling source
    query_expression.bal
Running executables

name=John college=Stanford GPA=4.1 school=Milpitas High School name=Bill college=Stanford GPA=3.9 school=San Jose High School

from子句的工作方式与foreach语句类似。它从一个可迭代值创建一个迭代器,然后将变量绑定到迭代器返回的每个值。let子句绑定变量。where子句提供了一种执行条件执行的方法,该方法可以引用from子句绑定的变量。当where条件的值为false时,迭代跳过以下子句。对每次迭代计算select子句,此示例中查询表达式的结果是一个列表,其成员是select子句的结果。

云原生技术集成

以前,开发人员只是编写程序、构建程序并运行它。但现在他们有各种各样的运作方式。它可以在裸机或虚拟机中。或者,程序可以打包为容器并部署到Kubernetes和service mesh这样的平台中,或者作为无服务器程序运行。但是,这些部署选项不是开发人员编程经验的一部分。开发人员必须以特定的方式编写代码,才能在给定的执行环境中良好地工作,而将其从编程问题中移除并不好。

Docker帮助将应用程序及其依赖项打包在二进制映像中,这些二进制映像可以在各种位置运行,无论是本地、公共云还是私有云。要创建优化的映像,开发人员必须遵循一组最佳实践,否则,所构建的映像将会很大,安全性较差,并存在许多其他缺点。

Ballerina编译器能够从应用程序源代码创建优化的Docker图像。添加@docker:Config{}到一个服务,生成Dockerfile和Docker映像。

import ballerina/http;
import ballerina/docker;

@docker:Config {
   name: "hello",
   tag: "v1.0"
}
service Hello on new http:Listener(8080) {
   resource function hi(http:Caller caller, http:Request request) returns error? {
       check caller->respond("Hello World!");
   }
}

$ ballerina build hello.bal
Compiling source
    hello.bal

Generating executables
    hello.jar

Generating docker artifacts...
    @docker          - complete 2/2

    Run the following command to start a Docker container:
    docker run -d -p 8080:8080 hello:v1.0

Kubernetes是一个用于自动化部署、扩展和管理容器化应用程序的开源平台。要在Kubernetes中部署和运行程序,开发人员需要创建一组YAML文件。对于一般开发人员来说,创建这些YAML文件并不容易。Ballerina编译器能够在编译源代码时创建这些YAML文件。

import ballerina/http;
import ballerina/kubernetes;

@kubernetes:Service {
   serviceType: "NodePort"
}
@kubernetes:Deployment {
   name: "hello"
}
service Hello on new http:Listener(8080) {
   resource function hi(http:Caller caller, http:Request request) returns error? {
       check caller->respond("Hello World!");
   }
}

向Ballerina服务添加@kubernetes:Deployment{}注释将生成Kubernetes部署YAML,这是将hello应用程序部署到Kubernetes所必需的。添加@kubernetes:Service{}注释将生成Kubernetes服务YAML。在这个场景中,我们将serviceType设置为‘NodePort’,以便通过nodeIP:Port访问hello服务。

$ ballerina build hello.bal
Compiling source
    hello.bal

Generating executables
    hello.jar

Generating artifacts...

    @kubernetes:Service        - complete 1/1
    @kubernetes:Deployment        - complete 1/1
    @kubernetes:Docker            - complete 2/2
    @kubernetes:Helm            - complete 1/1

    Run the following command to deploy the Kubernetes artifacts:
    kubectl apply -f hello/kubernetes

    Run the following command to install the application using Helm:
    helm install --name helloworld hello/kubernetes/helloworld

同样,开发人员可以使用Ballerina的内置注释将程序部署到OpenShift、Istio和Knative等平台上,或者作为AWS Lambda函数。

序列图

序列图是可视化地描述服务交互方式的最佳方式。芭蕾舞女演员的

对并发性和网络交互的抽象和语法进行了设计,以便与序列图有密切的对应关系。在Ballerina中,使用与非远程方法不同的语法(->)调用远程方法。在序列图中,它被描述为一个从工人的生命线到客户的对象生命线的水平箭头。

一般来说,开发人员会编写一次代码,然后多次读取它。在许多情况下,代码由另一个开发人员阅读,而不是最初编写代码的人。拥有这些自动的可视化表示将帮助开发人员理解程序交互。

Ballerina IDE插件(例如VSCode插件)可以从源代码动态生成序列图。

更多的阅读

在本文中,我们了解了Ballerina的独特特性如何使应用程序开发人员能够编写云本地应用程序。

有关更广泛的语言设计原则,请参考Ballerina语言规范。

在Ballerina的示例页面中可以找到使用内建数据类型(如JSON/XML)和其他基于网络的功能的各种示例。

关于作者

Lakmal Warusawithana是WSO2的高级总监/开发者关系。Lakmal在开源、云和DevOps技术方面有很长的工作历史,并且是Apache Stratos PaaS项目的副总裁。Lakmal是Ballerina(一种用于网络分布式应用程序的开源编程语言)的容器化和部署编排的架构师。Lakmal还在许多活动中进行了展示,包括ApacheCon、CloudOpen、QCon、JaxLondon、云博览会、Cloudstack协作会议、WSO2Con、KubeCon、ContainerCamp、研工周、API峰会和许多技术会议。

原文:https://www.infoq.com/articles/cloud-native-programming-ballerina/

本文:http://jiagoushi.pro/node/1351

讨论:请加入知识星球【快速和低代码开发】或者小号【it_training】或者QQ群【11107767】

Tags
 
Article
知识星球
 
微信公众号
 
视频号