clojure_改善您的OpenWhisk Clojure应用程序

在前两个教程中,您学习了如何使用Clojure(一种基于Lisp的功能编程语言)编写基本的OpenWhisk应用程序,从而为OpenWhisk应用程序创建操作。 在本教程中,我将通过向您展示如何改进任何此类应用程序来结束本系列。 首先,您将学习如何支持包含双引号的参数。 然后,我将向您展示如何使用永久数据库(Cloudant)而不是变量来存储信息。

构建应用程序所需的条件

本教程以“使用Clojure编写OpenWhisk动作”系列的前两个教程中的信息为基础, 第1部分,使用此Lisp方言为OpenWhisk编写简洁明了的代码第2部分,将Clojure OpenWhisk动作连接到有用的序列中 ,因此我建议先阅读这些内容。 此外,您将需要:

  • OpenWhisk和JavaScript的基本知识(Clojure是可选的;本教程说明了需要时所需要的内容)
  • 一个免费的Bluemix帐户( 在此处注册

运行应用程序 获取代码

函数式编程鼓励您隔离副作用,并将其与业务逻辑分开。 这导致应用程序具有更高的模块化,更易于测试和易于调试。

包含引号的参数

在第1部分中,我介绍了main.js JavaScript:

// Get a Clojure environment
var cljs = require('clojurescript-nodejs');


// Evaluate the action code
cljs.evalfile(__dirname + "/action.cljs");


// The main function, the one called when the action is invoked
var main = function(params) {

  var clojure = "(ns action.core)\n ";

  var paramsString = JSON.stringify(params);
  paramsString = paramsString.replace(/"/g, '\\"');

  clojure += '(clj->js (cljsMain (js* "' + paramsString + '")))';

  var retVal = cljs.eval(clojure);
  return retVal;
};

exports.main = main;

该脚本使用非常简单的解决方案将参数传递给Clojure:

var paramsString = JSON.stringify(params);
paramsString = paramsString.replace(/"/g, '\\"'); 
…
'(js* "' + paramsString + '")))';

此解决方案可以产生一个字符串,例如{\"num\": 5, \"str\": \"whatever\"} 。 双反斜杠( \\ )转换为单个反斜杠(即转义字符)。 生成的Clojure代码是(js* " {\"num\": 5, \"str\": \"whatever\"}") 。 由于js*评估它作为JavaScript获得的字符串参数,因此使我们回到了原始参数{"num": 5, "str": "whatever"} 。 问题是,如果字符串参数之一已经包含双引号( " ),则它将与引号完全一样地对待,从而导致诸如{"num": 5, "str": "what"ever"}这样的表达式{"num": 5, "str": "what"ever"}和语法错误。 从理论上讲,您可以通过使用js/<var name>语法来访问带有参数的变量来解决此问题,但是由于某些原因,这在OpenWhisk中不起作用。

在第3部分中,我介绍了fixHash函数,该函数遍历参数(包括任何嵌套的数据结构)并查找字符串。 在字符串中,它将所有双引号替换为\\x22 。 第一个反斜杠转义第二个反斜杠,因此实际值为\x22 。 该值最终会转换为ASCII字符0x22(十进制34,即双引号),但是这种情况会在以后发生,因此replace方法不会修改这些字符。

// Fix a hash table so it won't have internal double quotes
var fixHash = function(hash) {
	if (typeof hash === "object") {
		if (hash === null) return null;
		if (hash instanceof Array) {
			for (var i=0; i<hash.length; i++)
				hash[i] = fixHash(hash[i]);
			return hash;
		}
		
		var keys = Object.keys(hash) 
		for (var i=0; i<keys.length; i++)
			hash[keys[i]] = fixHash(hash[keys[i]]);
		return hash;
	}

	if (typeof hash === "string") {
		return hash.replace(/"/g, '\\x22');
	}

	return hash;
		
};

保存到数据库

在几分钟不使用时重置为初始值的库存管理系统不是很有用。 因此,下一步是设置一个对象存储实例以存储数据库值( inventory_dbase操作中的dbase变量):

  1. 在Bluemix控制台中,单击Menu图标,然后转到Services> Data&Analytics
  2. 单击创建数据和分析服务,然后选择Cloudant NoSQL DB
  3. 将服务命名为“ OpenWhisk-Inventory-Storage”,然后单击“ 创建”
  4. 创建服务后,将其打开,然后单击服务凭证>新建凭证
  5. 将新凭据命名为“库存应用”,然后点击添加
  6. 单击查看凭据,然后将凭据复制到文本文件中。
  7. 选择管理>启动
  8. 单击数据库图标,然后单击创建数据库
  9. 将数据库命名为“ openwhisk_inventory”。
  10. 单击“ 所有文档”行中的加号图标,然后选择“ 新建文档”
  11. dbase.json的内容复制到文本区域,然后单击创建文档

有两种方法可以将Cloudant数据库与应用程序结合。 您可以Cloudant行动要么添加到序列,或修改inventory_dbase行动。 我选择了第二个解决方案,因为它使我可以更改单个操作(因为所有数据库工作都集中在此)。

  1. Cloudantnpm库添加到package.json中的依赖项,并更新ventory_dbase操作的action.cljs文件:
    (ns action.core)
    
    (def cloudant-fun (js/require "cloudant"))
    
    (def cloudant (cloudant-fun "url goes here"))
    
    (def mydb (.use (aget cloudant "db") "openwhisk_inventory"))
    
    
    
    ; Process an action with its parameters and the existing database
    ; return the result of the action
    (defn processDB [action dbase data]
      (case action
        "getAll" {"data" dbase}
    
        "getAvailable" {"data" (into {} (filter #(> (nth % 1) 0) dbase))}
    
        "processCorrection" (do
          (def dbaseNew (into dbase data))
          {"data" dbaseNew}
        )
    
        "processPurchase" (do
          (def dbaseNew (merge-with #(- %1 %2) dbase data))
          {"data" dbaseNew}
        )
    
        "processReorder" (do
          (def dbaseNew (merge-with #(+ (- %1 0) (- %2 0)) dbase data))
          {"data" dbaseNew}
        )
    
        {"error" "Unknown action"}
      )   ;  end of case
    )   ; end of processDB
    
    
    
    (defn cljsMain [params] (
        let [
          cljParams (js->clj params)
          action (get cljParams "action")
          data (get cljParams "data")
          updateNeeded (or (= action "processReorder")
                           (= action "processPurchase")
                           (= action "processCorrection"))
        ]
    
        ; Because promise-resolve is here, it can reference
        ; action
        (defn promise-resolve [resolve param] (let
          [
            dbaseJS (aget param "dbase")
            dbaseOld (js->clj dbaseJS)
            result (processDB action dbaseOld data)
            rev (aget param "_rev")
          ]
            (if updateNeeded
              (.insert mydb (clj->js {"dbase" (get result "data"),
                                      "_id" "dbase",
                                      "_rev" rev})
                #(do (prn result) (prn (get result "data")) (resolve (clj->js result)))
              )
              (resolve (clj->js result))
            )
          )   ; end of let
        )   ; end of defn promise-resolve
    
    
        (defn promise-func [resolve reject]
          (.get mydb "dbase" #(promise-resolve resolve %2))
        )
    
        (js/Promise. promise-func)
      )   ; end of let
    )    ; end of cljsMain

让我们看一下action.cljs的一些更有趣的部分。

您需要获取数据库。 在JavaScript中,您可以这样编码:

var cloudant_fun = require("cloudant ");
var cloudant = cloudant_fun(<<<URL>>>);
var mydb = cloudant.db.use("openwhisk_inventory ");

相同代码的Clojure版本相似,但有一些区别。 首先, require是一个JavaScript函数。 要访问它,您需要使用js名称空间对其进行限定(上述步骤12中清单3的行):

(def cloudant-fun (js/require "cloudant"))

下一行(第5行)非常标准。 URL是数据库凭据中的URL参数:

(def cloudant (cloudant-fun <<URL GOES HERE>>))

要从JavaScript对象获取成员,请使用aget 。 要使用对象的方法,可以使用(.<method> <object> <other parameters>) 。 参见第7行:

(def mydb (.use (aget cloudant "db") "openwhisk_inventory"))

从数据库读取和写入都是异步操作。 这意味着您不能简单地运行它们并将结果返回给调用方(OpenWhisk系统)。 相反,您需要返回Promise对象 。 该构造函数接受一个参数-调用该函数以启动需要结果的进程。 从Clojure调用JavaScript对象的构造函数的语法是(js/<object name>. <parameters>) 。 参见第75行:

(js/Promise. promise-func)

提供给Promise对象的构造promise-func的函数是promise-func 。 它接收两个参数。 一种是在成功的情况下调用的函数(一个参数,即操作的结果)。 另一个是发生故障时要调用的函数(也是一个参数,错误对象)。 在这种情况下,该函数获取dbase文档,然后使用成功函数和该文档调用promise-resolve 。 匿名函数( #(promise-resolve resolve %2) )的第一个参数是错误(如果有)。 因为这是一个演示程序,所以为了简单起见,我们忽略了错误。 参见第71-73行:

(defn promise-func [resolve reject]
      (.get mydb "dbase" #(promise-resolve resolve %2))
    )

promise-funcpromise-resolve都在cljsMain中定义。 原因是promise-resolve需要action参数的值。 通过在cljsMain中定义这些函数,您可以仅使用该局部变量,而不必将其拖向整个调用链。 参见第52-55行:

(defn promise-resolve [resolve param] (let
      [
        dbaseJS (aget param "dbase")
        dbaseOld (js->clj dbaseJS)

获取或修改数据的函数是processDB 。 此函数封装了第1部分中介绍的功能。请参见第56行:

result (processDB action dbaseOld data)

由于Cloudant使用算法来确保状态一致性,因此修订( _rev )是更新数据库所必需的。 阅读文档时,会得到当前的修订版( _rev )。 编写更新的版本时,必须向Cloudant提供要更新的修订。 同时,如果另一个进程更新了文档,则版本将不匹配,并且更新将失败。 参见第57行:

rev (aget param "_rev")

如果数据已修改,请更新Cloudant。 参见第59行:

(if updateNeeded

向数据库提供新数据,文档名称和要更新的修订版。 参见第60-62行:

(.insert mydb (clj->js {"dbase" (get result "data"),
                                  "_id" "dbase",
                                  "_rev" rev})

更新完成后(我们仅假设更新成功;这是一个教育示例,而不是生产代码),请运行以下功能。 注意添加调试打印输出的机制。 使用do可以计算多个表达式,可以调用任意数量的prn函数来打印所需的信息,然后将实际需要的表达式放在最后。

在这种情况下,您调用通过Promise对象获得的resolve函数。 由于此函数在JavaScript中,因此需要接收一个JavaScript对象,而不是Clojure对象,因此请使用clj->js 。 参见第63-64行:

#(do (prn result) (prn (get result "data")) (resolve (clj->js result)))
          )

如果不需要更新Cloudant,只需运行resolve功能。 参见第65-66行:

(resolve (clj->js result))
        )

编译Clojure代码

当前,我们将Clojure代码发送到OpenWhisk,并在Node.js应用程序重新启动时在此处进行编译。 另一种选择是一次将Clojure本地编译为JavaScript,然后发送已编译的版本。 如果您有兴趣,可以在这里查看它的工作方式。 但是,此方法不能显着提高性能。

从下面的屏幕截图中可以看到,即使使用已编译的ClojureScript代码,该操作的第一次调用仍比后续操作花费更多的时间。

该屏幕截图显示了第一次调用动作

速度慢的原因是实际编译不是很耗费资源。 Clojure环境的创建是使用Clojure的资源密集型部分,即使Clojure代码本身已编译为JavaScript,这也是必需的。 编译器生成JavaScript使用一些繁重的库。

结论

到此为止,Clojure中的OpenWhisk操作系列结束了。 我希望我已经向您展示了将函数式编程用于函数即服务(FaaS)的一些优点。 归根结底,几乎每个应用程序都需要在某个时候使用副作用,但是功能编程鼓励您隔离这些副作用并将它们与业务逻辑分开。 这导致应用程序具有更高的模块化,更易于测试和易于调试。 通过将应用程序逻辑分为动作和序列,可以很容易地使应用程序的大规模结构更清晰,并编写单元测试以作为REST调用运行。


翻译自: https://www.ibm.com/developerworks/cloud/library/cl-clojure-openwhisk3/index.html

Openwhisk 概览 Openwhisk 概览 1. Openwhisk架构 openwhisk是一个事件驱动的计算平台,也被用在serveless和fass领域,来响应事件调用。事件通常包括: 数据库记录的修改 IoT传感器数据上传 GitHub代码仓库的新提交 HTTP调用 这些内部或者外部的事件通过trigger和rules,最终到达actions来响应这些事件。 Actions 通常是一些代码片段, ... 阅读详情

相关推荐

cljs-node-io:用于NodeJSClojureScript IO库

[cljs-node-io "1.1.2"] cljs-node-io {:mvn/version "1.1.2"} 这是clojure.java.io到clojurescript的端口,对nodejs来说是有意义的。 目的是使Clojure程序员感到宾至如归,因此大多数事物都具有与jvm相同的签名和语义。 但是,许多事情在内部必定有不同的工作方式,从而产生一些后果。 您可以 还包括: 与Java具有相同api的经过精化的文件 ++吐 节点流,子进程上的包装器 便捷功能使您的脚本编写和替换体验更加愉快 用编译,以便所有的核心异步都是引导友好的 在您的REPL会话和脚本中 ( require '[cljs-node-io.core :as io :refer [slurp spit]]) ( def data [{ :foo 42 } { :foo 43 }]) ( spit " data.edn " data) ( = data ( read-string ( slurp " data.edn " ))) ; => true 在您的应用中 ; ; write asynchrono

openwhisk部署

此处克隆项目,我有遇到一些由于网络的报错,比如connection refused。处理方法是多次重复,以及尝试着切换了另一个网络就行了(比如刚开始是连接的校园网,不行的话就切换成手机热点,然后就克隆成功了)。后面的内容会慢慢添上。将openwhisk项目克隆到本地。并结合自己部署时遇到的报错。

whisper211的博客 821

VSCode搭配Keil开发STM32:从环境配置到代码跳转全流程(避坑指南)

本文详细介绍了如何使用VSCode搭配Keil进行STM32开发,从环境配置到代码跳转的全流程指南。通过安装必备插件和正确配置项目文件,开发者可以在VSCode中享受现代编辑器的便捷功能,同时保留Keil的编译调试能力,提升嵌入式开发效率。

weixin_28832121的博客 220

OpenWhisk部署指南

OpenWhisk是开源的Serverless平台,Github上的地址,虽然Github上有着部署的相关文档,但还是遇到了不少的问题,将安装过程及问题记录如下。 首先是将项目Clone到本地,进入OpenWhisk目录下 # Install git if it is not installed sudo apt-get install git -y # Clone openwhisk git clone https://github.com/apache/openwhisk.git openwhisk

weixin_51971301的博客 5893

Apache OpenWhisk架构概述

本文讲的是Apache OpenWhisk架构概述【编者的话】Apache OpenWhisk是一个开源FaaS平台。现在,AWS Lambda是FaaS最流行的实现。 在开源组件的选择中,OpenWhisk是最好的选择。OpenWhisk是一个健壮的、可扩展的平台,支持数千并发触发器和调用。这篇文章将探索OpenWhisk设计和架构,识别各种组件及其作...

weixin_33757911的博客 609

Apache OpenWhisk 安装与配置指南

Apache OpenWhisk 是一个开源的无服务器云平台,允许开发者通过编写函数来构建云应用程序。它提供了丰富的编程模型,用于从函数创建无服务器API、将函数组合成无服务器工作流,以及使用规则和触发器将事件连接到函数。OpenWhisk 支持多种编程语言,通过容器化技术运行函数,确保了高效率和灵活性。 ### 主要编程语言 - Scala - JavaScript - Python - Sh

gitblog_00712的博客 1188

使用 Clojure 编写 OpenWhisk 操作,第 3 部分

在前两篇教程中,您学习了如何使用 Clojure(一种基于 Lisp 的函数式编程语言)编写一个基本的 OpenWhisk 应用程序,从而为 OpenWhisk 应用程序创建操作。本教程是本系列的最后一部分,将展示如何改进任何这类应用程序。首先,您将学习如何支持包含双引号的参数。然后,我将展示如何使用永久型数据库 (Cloudant) 代替变量来存储信息。构建您的应用程序需要做的准备工作本教程以“...

weixin_34198762的博客 162

探秘未来云计算:OpenWhisk 开源项目深度解析

探秘未来云计算:OpenWhisk 开源项目深度解析 1、项目介绍 OpenWhisk 是一款领先云原生的分布式事件驱动编程服务,让你能够在云端轻松上传事件处理器并响应各种事件。其理念是将计算与服务器分离,实现真正的“无服务器”架构。由IBM 主导,并以开源形式开放给全球开发者,OpenWhisk 现已成长为一个强大的社区项目。 2、项目技术分析 OpenWhisk 提供了多种运行时环境,包括 J...

gitblog_00006的博客 415

OpenWhisk高级特性解密:解锁无服务器架构的无限可能

还在为无服务器架构的扩展性而烦恼?OpenWhisk的SPI扩展机制、Web Actions和API网关集成将彻底改变你的开发体验!本文将为你揭秘OpenWhisk的高级特性,助你构建更强大的云原生应用。 **读完本文你将获得:** - SPI扩展机制的完整理解与实践指南 - Web Actions高级特性的深度应用技巧 - API网关与无服务器函数的完美集成方案 - 注释系统的灵活配置方法

gitblog_01092的博客 430

openwhisk:构建云应用的serverless函数平台

OpenWhisk 是一个开源的 serverless 函数平台,旨在帮助开发者构建云应用。它提供了一个丰富的编程模型,让开发者可以从函数创建 serverless API,将函数组合成 serverless 工作流,以及使用规则和触发器将事件连接到函数。OpenWhisk 由 Apache 软件基金会维护,遵循 Apache-2.0 许可证,保障了其开放性和灵活性。 ## 项目技术分析 Op

gitblog_00663的博客 751

揭开serverless框架OpenWhisk的工作原理

使用像OpenWhisk这样的无服务器平台几乎就像是在练习黑魔法。你插入代码,它就按你想要的频率执行代码。你永远不会按部署键,永远不用担心缩放。你也不需要关注它的底层设施。这么魔幻吗?不完全是! ​ 本文将解释在OpenWhisk中调用操作时会发生什么。它将解释OpenWhisk部署的所有组件,并概述每个组件在系统中所扮演的角色。今天我们将揭开地下的秘密,古老的咒语。其实也不是什么秘密。因为OpenWhisk开源! ​ 作为一个开源项目,OpenWhisk站在巨人的肩膀上。那些巨人可真高!Nginx、Kaf

程序员石磊 2723

3个问题揭示视频文字提取终极方案:videocr免费完整指南

当视频中的关键信息被硬编码为字幕时,如何快速提取这些文字内容?传统的手动转录不仅耗时费力,而且准确率难以保证。videocr视频文字提取工具正是为解决这一痛点而生,它将OCR技术与视频处理完美结合,为技术爱好者和普通用户提供了**快速**、**免费**的**完整指南**。 > **核心关键词**:videocr视频文字提取 > **长尾关键词**:Python视频文字识别、硬编码字幕提取、多

gitblog_00198的博客 863

Apache OpenWhisk 项目使用教程

Apache OpenWhisk 项目使用教程 1. 项目的目录结构及介绍 Apache OpenWhisk 是一个开源的分布式事件驱动编程服务。以下是项目的主要目录结构及其介绍: openwhisk-release/ ├── README.md ├── docs/ │ ├── ... │ └── ... ├── tools/ │ ├── ... │ └── ... ├── ans...

gitblog_00207的博客 352

Serverless开源框架对比

Kubernetes 的蓬勃发展由催生了一系列以它为基础的Serverless框架,目前开源的Serverless框架大多以Kubernetes为基础。接下来,将针对性介绍2020年应用较为广泛的几种Serverless框架. 一、Openfass OpenFaaS是一个构建无服务器功能的框架,它拥有对指标的第一个类支持。任何流程都可以打包为一个功能,使你能够使用一系列web事件,而无需重复的样板化编码。 优点: 通过UI入口和单击安装轻松使用 为Linux或Windows的任何语言编写函..

隔壁老瓦的专栏 4185

Apache OpenWhisk 使用教程

Apache OpenWhisk 使用教程 项目介绍 Apache OpenWhisk 是一个开源的分布式无服务器平台,能够运行事件驱动型代码,即“函数”。这些函数可以通过 HTTP 调用,也可以由各种事件触发,如数据库更新、消息队列消息、监控警报等。OpenWhisk 支持多种编程语言,并且可以轻松扩展以处理高并发请求。 项目快速启动 环境准备 在开始之前,请确保您的系统上已安装 Docker ...

gitblog_01134的博客 586

Apache OpenWhisk 项目教程

Apache OpenWhisk 项目教程 1. 项目的目录结构及介绍 Apache OpenWhisk 是一个开源的分布式事件驱动编程服务。项目的目录结构如下: openwhisk-release/ ├── README.md ├── docs/ │ ├── README.md │ └── ... ├── tools/ │ ├── README.md │ └── ... ├── ...

gitblog_00325的博客 767

SMUDebugTool代码架构深度剖析:WinForms + ZenStates-Core 混合工具设计终极拆解

SMUDebugTool 是一款面向 AMD Ryzen 处理器的专用超频与 SMU 调试工具,可以手动读写 SMU、PCI、CPUID、MSR、Power Table 等底层参数。它的核心架构是 **WinForms 界面层 + ZenStates-Core 硬件通信层**的混合设计:界面用经典 WinForms 表单组织功能,底层统一由预编译的 ZenStates-Core 库驱动物理内存、M

gitblog_00018的博客 385

Neurite FractalGPT深度体验:图形思维与非线性记忆的革命性突破

Neurite(GitHub加速计划)是一款融合分形图形理论与人工智能的非线性思维映射工具,专为AI代理、网络链接、笔记和代码管理设计。通过其独特的分形图结构,用户可以构建类似大脑神经元网络的知识图谱,实现信息的非线性关联与记忆。 ## 🌟 分形思维:突破线性认知的边界 传统思维导图受限于树状结构,难以表达复杂的多维度关联。Neurite采用**曼德博集合分形算法**(实现于[js/mand

gitblog_01094的博客 445

VitePress Markdown 文件包含:利用标题锚点(Header Anchor)精准引用文档片段

在 VitePress 的 Markdown 文件包含(File Inclusion)机制中,除了按行号区间和代码区域(region)选取内容外,还可以直接用**标题锚点**(header anchor / heading id)来引用某个标题下的完整小节。这一能力让文档作者可以把一个长文档拆成可复用的"部件"(partial),并在任意页面中精确嵌入其中一段,且**被包含文件中的相对链接和图片会

gitblog_01065的博客 495

RavenDB文档建模最佳实践:避免常见陷阱的专家指南

RavenDB作为一款ACID文档数据库,提供了灵活的数据模型和强大的查询能力,帮助开发者构建高性能的应用程序。本文将分享RavenDB文档建模的最佳实践,帮助你避免常见陷阱,设计出高效、可扩展的数据结构。 ## 理解RavenDB的文档模型 RavenDB采用面向文档的数据模型,每个文档都是一个独立的JSON对象,包含相关的数据和元数据。与传统关系型数据库不同,文档数据库不需要预定义模式,这

gitblog_00429的博客 446
上一篇: rational rosa_Rational Application Development认证准备,第5部分,运行Java应用程序
下一篇: vaadin8开发手册_在云端使用Vaadin开发全栈Java应用程序
cusi77914
博客等级 码龄12年 78粉丝 0原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值