阿里云函数计算 + Aglio 实现 API Blueprint markdown 渲染
本文首发于我的博客 阿里云函数计算 + Aglio 实现 API Blueprint markdown 渲染
这次想聊聊我在写的站里的一个核心的功能——API 文档的编辑与展示。我最初的想法是,使用 Markdown 来编写文档。但是这样在格式上难免不好统一。跟 @BillSJC 聊的时候,他向我推荐了 Swagger 和 API Blueprint。这两个都是 API 文档自动生成工具。对比过后,我决定选择 API Blueprint,因为它是使用类似 markdown 的语法进行文档编写的,相比 Swagger 的使用 YAML,API Blueprint 写的 markdown 可读性更强一些:
# GET /message + Response 200 (text/plain) Hello World!
地鼠不友好
API Blueprint 的官网上 https://apiblueprint.org/ 推荐了从文档编辑到解析、渲染的诸多工具支持。
我的需求是找到一个 API Blueprint 的渲染工具,将我在 Apicon 编写的 markdown 渲染转换成 HTML 文档。但官网展示的这些工具大多都是 Node.js 编写的,我并没有找到支持 Golang 的工具。但我又不想额外在服务器上再维护一个容器,用于专门运行转换 markdown 的 Node.js 代码。
这时,@BillSJC 向我介绍了 Serverless 的概念,同时推荐了阿里云的函数计算。
Serverless?
从字面上意思来理解 Serverless,即无服务器。
将这个概念放在阿里云的函数计算服务上,即我只需要将我实际的业务代码上传到函数计算服务上,然后通过约定好的触发器去调用这个服务。在这其中,我并不需要去关心服务器相关的硬件配置,环境配置,以及运维等这些事情。我只需要专注于业务代码即可。
这可能和我们平时做的 RESTful API 有些相似,RESTful API 其实也可以看做是通过一个 HTTP 触发器,去调用相关服务。虽然二者的最终结果都是一样的,但 RESTful API 的背后,往往是我们的服务器一直在监听是否有请求,一旦有请求来了,就进行处理;而函数计算这个“事件驱动”更像是一个被动的东西,被触发后执行一段事先设定好的步骤,然后结束。
就像一个函数一样,每次执行都是差不多的步骤和内容,前后之间的两次调用并不会有太大的关联和影响。我们可以来看下大厂都在用它在干什么:
新浪微博 - 调用函数将用户上传的图片进行个性化处理
115 - 触发函数对用户上传的日志进行压缩、转换
芒果 TV - 触发函数记录视频文件的属性信息
石墨文档 - 使用函数来处理多用户文档编辑的冲突
这么一看我是选对了,新浪都用来处理图片了,那我用来处理 markdown 岂不也是合情合理?嘛,开始吧!
Aglio 渲染 markdown
API Blueprint 的渲染器我之所以选择 Aglio https://github.com/danielgtaylor/aglio ,是因为它不仅可以自定义主题,还可以自定义模板。这样我就可以将页面中我不需要的标题、导航栏等内容删除,仅保留内容主体。同时 Aglio 不仅支持 CLI 使用,还可以当做一个包,放在项目中调用。
我们先在本地实现一个 markdown 的渲染,再将它改写成函数计算的模式。
先把 Aglio 给拉下来:
yarn add aglio
然后我们就可以开始编写主要的代码了:
let aglio = require('aglio') let blueprint = ` # GET /message + Response 200 (text/plain) Hello World! ` options = { themeTemplate: 'index.jade' }; aglio.render(blueprint, options, function (err, html, warnings) { if (err) return console.log(err); if (warnings) console.log(warnings); console.log(html); });
这里我使用的是 Aglio 的默认主题,你可以在 Aglio GitHub Repo 的olio-theme
分支下载到主题需要的index.jade
与mixins.jade
这两个模板文件。然后就可以开始魔改这两个jade
文件了:
doctype include mixins html head meta(charset="utf-8") style!= self.css body.preload div .row .content block content +Content('primary', false)
我这边将导航栏,标题栏、不用的 JavaScript 都去掉了。然后mixins.jade
也是按需修改,相关英文的提示我也进行了汉化。最后的效果是这样的:
下面我们来将其部署到阿里云函数计算上。
部署到阿里云函数计算
因为我们这里的文件很多,故使用阿里云提供的fun
工具来进行部署。
Mac 安装fun
:
brew install fun
安装好后,先输入fun config
来跟着命令行提示进行基本的设置:
Aliyun Account ID (阿里云ID) Aliyun Access Key ID *************** (AK) Aliyun Secret Access Key *************** (SAK) Default region name cn-hongkong (地区,我是选择跟自己轻量应用一样的地区,方便直接内网调用) The timeout in seconds for each SDK client invoking 300 (上传代码超时时间) The maximum number of retries for each SDK client 3 (上传失败后的尝试次数) Allow to anonymously report usage statistics to improve the tool over time? No (是否帮助阿里云改进此工具)
有坑注意
第一项Aliyun Account ID
填写的是阿里云的账号 ID,并不是登录时填写的账号。账号 ID 可以在基本资料
-安全设置
中找到。
之后就可以执行fun init
来初始化一个服务了。它会给我们创建template.yml
文件,我们进行如下配置:
ROSTemplateFormatVersion: '2015-09-01' Transform: 'Aliyun::Serverless-2018-04-03' Resources: Aglio-Function: Type: 'Aliyun::Serverless::Service' Properties: Description: 'Markdown render' Aglio-Function: Type: 'Aliyun::Serverless::Function' Properties: Handler: index.handler Runtime: nodejs8 CodeUri: './' Events: http-test: Type: HTTP Properties: AuthType: ANONYMOUS Methods: ['POST']
注意这里的Events
,即创建了一个 HTTP 触发器,仅允许使用 POST 进行调用,且不做鉴权。
有坑注意
在所有触发器中,HTTP 触发器只能在创建服务时添加,之后无法再添加。因此,在使用fun
部署项目时,我们通过template.yml
文件创建。
下面编写index.js
,将我们的业务代码与阿里云函数计算的入口进行对接:
var getRawBody = require('raw-body'); module.exports.handler = function(req, resp, context) { let options = { themeTemplate: 'index.jade' }; let aglio = require('aglio') getRawBody(req, function(err, body) { aglio.render(body.toString(), options, function (err, html, warnings) { if (err) return console.log(err); if (warnings) console.log(warnings); resp.send(html); }); });
这里通过引入require('raw-body');
来获取请求的 body,通过 Aglio 处理后返回。
之后执行fun deploy
部署服务。注意:node_modules
文件夹的内容也要一并上传,函数计算不会再次进行拉包。
魔改 Aglio
在阿里云函数计算的控制台中调试我们刚才部署的函数,发现会报 502 错误。
问题出在 Aglio 在渲染 markdown 时,会往node_modules
目录下写入一个JavaScript
的临时缓存文件。但阿里云的函数计算只开放了/tmp
目录下的写入权限,其余都是只读。
那么这里我只能魔改 Aglio 源码了。还好函数计算不会使用包管理拉包,它就只用我们传上去的node_modules
。
问题出在 Aglio 的文件写入那里,我们只需更改写文件的目录即可:/node_modules/aglio-theme-olio/lib/main.js
文件,第 144 行与第 249 行:
// Line 144 //compiledPath = path.join(ROOT, 'cache', (sha1(key)) + ".css"); compiledPath = path.join('/tmp', (sha1(key)) + ".css"); // Line 249 //compiledPath = path.join(ROOT, 'cache', (sha1(key)) + ".js"); compiledPath = path.join('/tmp', (sha1(key)) + ".js");
然后写入的 JavaScript 的临时文件中,有使用require
引入node_modules
目录其它的包,这里的相对路径我们改成绝对路径,否则还是会报错 502。
还是在main.js
文件下,第 236 行:
// Line 236 //return compiled = "var jade = require('jade/runtime');\n" + (jade.compileFileClient(filename, options)) + "\nmodule.exports = compiledFunc;"; return compiled = "var jade = require('/code/node_modules/jade/runtime');\n" + (jade.compileFileClient(filename, options)) + "\nmodule.exports = compiledFunc;";
之后再部署,就可以看到啦~
总结一下吧~
又学到了新的玩意儿呢。对于一些功能单调、而又十分常用的功能,又不想专门放在服务器上花心思去维护,不妨可以尝试一下阿里云的函数计算。Serverless 这个东西,用得好真的可以解决很多问题。
阿里云的fun
工具很好用,让我能快速部署从而能进行调试。这一次下来也踩了很多坑,感觉阿里云的这个仅/tmp
目录可写的设定就很蠢。他为何不能改成当前代码执行目录可写呢?然后 Aglio 方面,Aglio 可以说是 API Blueprint 目前可定制度最高的工具了,这点不可否认。像临时缓存文件目录写死在node_modules
,这个从开发的角度其实也能理解啦。我一开始也不相信自己能魔改成功的哈哈。
然后关于函数计算的定价方面:
这个真的不用太担心,阿里云财大气粗。
每月免费次数 100 万次!!
128Mb 每月免费秒数:3200000秒!!换算一下大概是 37 天。也就是说我即使一个月内一秒一次地调用都够。
因此对于个人用户来说,函数计算真的是完全免费了。
这里需要注意一下阿里云内存的计算方式,他并不是按照函数运行时的实际内存占用计算的,而是按照你给函数运行环境设定的执行内存计算。
也就是说我设定 1024 Mb 的最大执行内存,即使实际调用时远远没有用到这么多,阿里云还是按照 1024 Mb 来计费。
推荐是设定实际运行内存占用的两倍大小。像我这个 markdown 渲染,一次实际占用内存 66 Mb 左右,因此我选择的是 128 Mb 的执行内存。
接下来要做的就是关掉函数计算的公网访问,然后让 ECS 从内网调用它。如果可以的话,再把函数计算的日志给打开,后面还要继续探索。(≧∇≦)ノ
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
初学HTML5的几点建议
初学HTML5的几点建议,最近几年,移动互联网发展正处在飞速增长的态势,Web前端开发工程师的岗位吸引了不少人,很多人通过学习来转行从事这个行业,作为Web前端开发中热门的语言HTML5的热度也一直居高不下,那么零基础的时候应该怎么学习呢? 前端初始的知识点,都不算很难,但是内容比较多,此时在学习的时候需要戒骄戒躁,一定要沉下心来,HTML为结构,CSS是样式,JS是功能等等,梳理好清晰的知识点思维导图,这部分内容其实并没有想象中的那么难。 在学习时还需要注意以下几个方面: 1、制定一个系统的学习计划。HTML5虽然难度不大,但是内容却比较多,在学习的过程中要循序渐进,一个比较常见的计划就是依次学习HTML5、CSS和JavaScript,JavaScript是学习的一个重点。一个好的学习计划,能够让自己在学习的时候更加专注,每完成一天的任务,都会有一种成就感,久而久之,对于编程语言的学习就会更有兴趣和动力。而且一个良好的学习计划,会让自己的效率提高不少。 2、实战编写能力是重中之重。实践是学习HTML5的重要环节,由于HTML5的细节比较多,要想详细的掌握这些内容一定是通过自己不断写...
- 下一篇
Spring Boot 2.X(十二):定时任务
简介 定时任务是后端开发中常见的需求,主要应用场景有定期数据报表、定时消息通知、异步的后台业务逻辑处理、日志分析处理、垃圾数据清理、定时更新缓存等等。 Spring Boot 集成了一整套的定时任务工具,让我们专注于完成逻辑,剩下的基础调度工作将自动完成。 通用实现方式 实现方式 描述 java.util.Timer Timer 提供了一个 java.util.TimerTask 任务支持任务调度。该方式只能按指定频率执行,不能在指定时间运行。由于功能过于单一,使用较少。 Quartz Quartz 是一个功能比较强大的调度器,支持在指定时间运行,也可以按照指定频率执行。缺点是使用起来相对麻烦。 Spring 框架自带的 Schedule 模块 可以看做是轻量级的 Quartz 静态定时任务 @Component @EnableScheduling public class StaticScheduleJob { /** * 上次开始执行时间点后5秒再次执行 */ @Scheduled(fixedRate = 5000) public void job3() { System.out....
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS8编译安装MySQL8.0.19
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2整合Thymeleaf,官方推荐html解决方案