关键要点
Ballerina 是一种新的编程语言和平台, 目标是让创建跨分布式端点的弹性服务变得更轻松.
Ballerina 使用了分布式系统原语的编译时抽象. 这为数据转换提供了类型安全性, 编译器可以生成构件, 如用于将应用部署到 Docker 和 Kubernetes 的 API 网关.
Ballerina 定义了一系列关键词来表示集成概念, 包括网络端点, 服务, 流式 SQL 以及 table,json 和 xml 原始类型. IDE 和其他工具可以基于这些语法元素从任意的 Ballerina 代码生成时序图.
Ballerina 是一种语言和平台的组合设计, 敏捷且易于集成, 旨在简化集成和微服务编程. 三年前, WSO2 的架构师发起了 Ballerina 项目, 以解决他们在为 EAI,ESB 和工作流产品 (如 Apache Synapse 和 Apache Camel) 构建集成流程时遇到的挑战.
使用 Ballerina 编写集成流程, 并将它们作为可伸缩微服务部署在 Kubernetes 上, 这是怎样的一种体验?
- # Ballerina tutorial config file with Twitter secrets
- clientId = ""clientSecret =""
- accessToken = ""accessTokenSecret =""
- // `http` 包是默认发行版标准库的一部分.
- // 这个包包含了一些对象, 注解, 函数和连接器,
- // 在代码中使用它们时使用 `http:` 命名空间来引用它们.
- import ballerina/http;
- // 将这个注解添加到服务上, 将基础路径从 `/hello` 改为 `/`.
- // 这个注解来自之前导入的包.
- @http:ServiceConfig {
- basePath: "/"
- }
- service<http:Service> hello bind {port:9090} {
- // `service` 代表了一个实现特定协议的 API, 并监听 `endpoint`.
- // 在这里, 服务名为 "hello", 并与端口 9090 上的一个匿名端点绑定.
- // 将这个注解添加到资源上来改变它的路径,
- // 并只接受 POST 请求.
- @http:ResourceConfig {
- path: "/",
- methods: ["POST"]
- }
- hi (endpoint caller, http:Request request) {
- // 这是服务的一个 `resource`.
- // 每个资源代表了一个可调用的 API 端点.`endpoint` 代表网络位置.
- // 对于资源来说, 调用客户端也是 `endpoint`, 并作为参数传入.
- // 这个服务也是一个 `endpoint`, 并扮演着监听器的角色.
- // 从入站请求中抽取消息.
- // getTextPayload()返回一个 string 和 error 的联合类型.
- // 操作符 `check` 的意思是, 如果返回的是一个字符串, 那么就赋值给左边的变量,
- // 否则的话把错误传到上一层.
- string payload = check request.getTextPayload();
- // 这里来自 `http` 包的一个数据结构,
- // 作为发送给调用者的响应消息.
- http:Response res;
- res.setPayload("Hello"+payload+"!\n");
- // 将响应消息发送给访问资源的客户端.
- // `->` 是一种特定的语法, 表示这个调用是一个网络调用.
- // `_` 表示忽略响应或错误.
- _ = caller->respond(res);
- }
- }
- $ ballerina run homer.bal
- ballerina: initiating service(s) in 'homer.bal'
- ballerina: started HTTP/WS endpoint 0.0.0.0:9090
- $ # 你也可以为. balx 创建一个链接可执行文件, 并单独运行它
- $ ballerina build homer.bal
- $ ballerina run homer.balx
- $ curl -X POST -d "Ballerina" localhost:9090
- Hello Ballerina!
- $ ballerina search twitter
- Ballerina Central
- =================
- |NAME | DESCRIPTION | DATE | VERSION |
- |-----------------| --------------------------------| ---------------| --------|
- |wso2/twitter | Connects to Twitter from Ball...| 2018-04-27-Fri | 0.9.10 |
- import ballerina/http;
- import wso2/twitter;
- import ballerina/config;
- // twitter 包定义了可与 Twitter API 发生交互的端点类型.
- // 我们需要使用 apps.twitter.com 的 OAuth 数据来初始化它.
- // 我们从 toml 文件中读取这些数据, 而不是把它们写在代码里.
- endpoint twitter:Client tweeter {
- clientId: config:getAsString("clientId"),
- clientSecret: config:getAsString("clientSecret"),
- accessToken: config:getAsString("accessToken"),
- accessTokenSecret: config:getAsString("accessTokenSecret"),
- clientConfig:{}
- };
- @http:ServiceConfig {
- basePath: "/"
- }
- service<http:Service> hello bind {port:9090} {
- @http:ResourceConfig {
- path: "/",
- methods: ["POST"]
- }
- hi (endpoint caller, http:Request request) {
- http:Response res;
- string payload = check request.getTextPayload();
- // 将请求转换成推文
- if (!payload.contains("#ballerina")){payload=payload+"#ballerina";}
- twitter:Status st = check tweeter->tweet(payload);
- // 转换, 生成 JSON 并传回
- json myJson = {
- text: payload,
- id: st.id,
- agent: "ballerina"
- };
- // 传回 JSON, 而不是普通文本
- res.setPayload(myJson);
- _ = caller->respond(res);
- }
- }
- $ curl -d "My new tweet" -X POST localhost:9090
- {"text":"My new tweet #ballerina","id":978399924428550145,"agent":"ballerina"}
- import ballerina/http;
- import wso2/twitter;
- import ballerina/config;
- // 这个端点是到外部服务的一个连接.
- // 断路器是这个连接的一个配置参数.
- // 断路器会在碰到特定错误码或出现 500 毫秒超时时断开.
- // 断路器会在 3 秒后恢复到初始状态.
- endpoint http:Client homer {
- url: "http://www.simpsonquotes.xyz",
- circuitBreaker: {
- failureThreshold: 0,
- resetTimeMillis: 3000,
- statusCodes: [500, 501, 502]
- },
- timeoutMillis: 500
- };
- endpoint twitter:Client tweeter {
- clientId: config:getAsString("clientId"),
- clientSecret: config:getAsString("clientSecret"),
- accessToken: config:getAsString("accessToken"),
- accessTokenSecret: config:getAsString("accessTokenSecret"),
- clientConfig: {}
- };
- @http:ServiceConfig {
- basePath: "/"
- }
- service<http:Service> hello bind {port: 9090} {
- @http:ResourceConfig {
- path: "/",
- methods: ["POST"]
- }
- hi (endpoint caller, http:Request request) {
- http:Response res;
- // 使用 var 来引用 http:Response 和 error 联合类型.
- // 编译器知道如何使用实际的类型.
- var v = homer->get("/quote");
- // 使用 match 处理异常情况或正常输出
- match v {
- http:Response hResp => {
- // if proper http response use our old code
- string payload = check hResp.getTextPayload();
- if (!payload.contains("#ballerina")){payload=payload+"#ballerina";}
- twitter:Status st = check tweeter->tweet(payload);
- json myJson = {
- text: payload,
- id: st.id,
- agent: "ballerina"
- };
- res.setPayload(myJson);
- }
- error err => {
- // 如果出现错误或断路器断开了, 就会调用这段代码
- res.setPayload("Circuit is open. Invoking default behavior.");
- }
- }
- _ = caller->respond(res);
- }
- }
- $ curl -X POST localhost:9090
- {"text":"Marge, don't discourage the boy! Weaseling out of things is important to learn. It's what separates us from the animals! Except the weasel. #ballerina","id":986740441532936192,"agent":"ballerina"}
- $ curl -X POST localhost:9090
- Circuit is open. Invoking default behavior.
- $ curl -X POST localhost:9090
- Circuit is open. Invoking default behavior.
- $ curl -X POST localhost:9090
- {"text":"It's not easy to juggle a pregnant wife and a troubled child, but somehow I managed to fit in eight hours of TV a day.","id":978405287928348672,"agent":"Ballerina"}
- import ballerina/http;
- import wso2/twitter;
- import ballerina/config;
- // 导入 kubernetes 包
- import ballerinax/kubernetes;
- endpoint twitter:Client tw {
- clientId: config:getAsString("clientId"),
- clientSecret: config:getAsString("clientSecret"),
- accessToken: config:getAsString("accessToken"),
- accessTokenSecret: config:getAsString("accessTokenSecret"),
- clientConfig:{}
- };
- // 我们创建一个单独的端点, 而不是使用内联的{port:9090}.
- // 我们需要这样做, 因为这样才能添加 Kubernetes 注解,
- // 告诉编译器生成一个 Kubernetes 服务, 并将其暴露出来.
- @kubernetes:Service {
- serviceType: "NodePort",
- name: "ballerina-demo"
- }
- endpoint http:Listener listener {
- port: 9090
- };
- // 让编译器生成 Kubernetes 部署文件和 Docker 镜像
- @kubernetes:Deployment {
- image: "demo/ballerina-demo",
- name: "ballerina-demo"
- }
- // 将配置文件传给镜像
- @kubernetes:ConfigMap {
- ballerinaConf: "twitter.toml"
- }
- @http:ServiceConfig {
- basePath: "/"
- }
- service<http:Service> hello bind listener {
- @http:ResourceConfig {
- path: "/",
- methods: ["POST"]
- }
- hi (endpoint caller, http:Request request) {
- // 资源相关代码不变
- }
- }
- $ ballerina build demo.bal
- @kubernetes:Service - complete 1/1
- @kubernetes:ConfigMap - complete 1/1
- @kubernetes:Docker - complete 3/3
- @kubernetes:Deployment - complete 1/1
- $ tree
- .
- demo.bal
- demo.balx
- kubernetes
- demo_config_map.yaml
- demo_deployment.yaml
- demo_svc.yaml
- docker
- Dockerfile
- twitter.toml
- $ kubectl apply -f kubernetes/
- configmap "hello-ballerina-conf-config-map" created
- deployment "ballerina-demo" created
- service "ballerina-demo" created
- $ kubectl get pods
- NAME READY STATUS RESTARTS AGE
- ballerina-demo-74b6fb687c-mbrq2 1/1 Running 0 10s
- $ kubectl get svc
- NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
- ballerina-demo NodePort 10.98.238.0 <none> 9090:31977/TCP 24s
- kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d
- $ curl -d "Tweet from Kubernetes" -X POST http://localhost:31977
- {"text":"Tweet from Kubernetes #ballerina", "id":978399924428550145, "agent":"Ballerina"}
来源: http://www.infoq.com/cn/articles/ballerina-integration-tutorial-part-2