《Nodejs开发加密货币》之七:入口程序app.js解读

入口程序app.js解读

发布本文时,比特币价格 ¥2873.95 / $443.95 。为什么一个凭空设计出来的加密货币如此受追捧?为什么微软、IBM等巨头纷纷进入?为什么尝试了解比特币的技术人员,都会被深深吸引?它到底有什么诱人之处?《Nodejs开发加密货币》,让我们一起探索其背后的密码。

《Nodejs开发加密货币》,目的是提供加密货币(亿书币)的详尽开发文档,涉及到使用Nodejs开发产品的方方面面,从前端到后台、从服务器到客户端、从PC到移动、从IO密集型到计算密集型、从中心化到去中心化、加密解密、区款链等各个环节。本系列文章,只有开始,没有结束…

亿书:为人类创作增添新动力!代码完全开源、文章免费分享。 官网 http://ebookchain.org

QQ交流群:185046161

前言

在入门文章部分,我们已经知道,Nodejs的应用最终都可以合并成一个文件,为了开发方便,才将其拆分成多个文件。

被拆分的那个文件,自然是我们重点研究的对象,通常这个文件就是App.js或server.js,大家称之为入口程序

显然Ebookcoin用的就是app.js。这一篇,我们就来阅读一下该文件,学习研究它的整体架构流程。

源码

地址: https://github.com/Ebookcoin/ebookcoin/blob/master/app.js

类图

js原本无类,因此它的类图并不好处理,仅能大致给出它与其他模块的关联关系。

解读

直接读代码看看。

1.配置处理

任何一个应用,都会提供一些参数。对这些参数的处理,有很多种方案。但总的来说,通常需要提供一种理想环境,即默认配置,同时给你一种方法自行修改。

(1)全局默认配置

通常默认参数较少时,可以硬编码到代码里。但更灵活的方式,就是使用单独文件。这里就使用了文件 ./config.json 来保存全局配置,如:

{
    "port": 7000,
    "address": "0.0.0.0",
    "serveHttpAPI": true,
    "serveHttpWallet": true,
    "version": "0.1.1",
    "fileLogLevel": "info",
    "consoleLogLevel": "log",
    "sharePort": true,
    ...

使用时,只需要require就可以了。源码:

var appConfig = require("./config.json"); // app.js 4行

不过,为了灵活性,默认值通常允许用户修改。

(2)使用commander组件,引入命令行选项

上一篇已经分享,commander是Nodejs第三方组件(使用npm安装),常被用来开发命令行工具,用法极为简单。源码:

// 1行
var program = require(‘commander‘);

// 19行
program
    .version(packageJson.version)
    .option(‘-c, --config <path>‘, ‘Config file path‘)
    .option(‘-p, --port <port>‘, ‘Listening port number‘)
    .option(‘-a, --address <ip>‘, ‘Listening host name or ip‘)
    .option(‘-b, --blockchain <path>‘, ‘Blockchain db path‘)
    .option(‘-x, --peers [peers...]‘, ‘Peers list‘)
    .option(‘-l, --log <level>‘, ‘Log level‘)
    .parse(process.argv);

这样,就可以在命令行执行命令时,加带-c,-p等选项,例如:

node app.js -p 8888

这时,该选项就以program.port的形式被保存,于是手动修改一下:

// 39行
if (program.port) {
    appConfig.port = program.port;
}

这是处理Nodejs应用全局配置的一种常用且简单的方式,值得学习。

更多内容,请阅读上一篇对commander组件的详细介绍。

2.异常捕捉

我们在第一部分总结时,特意提到异常要捕捉,这里我们很轻松就可以看出来,代码对全局异常处理的方式。

注意:对于domain模块,已经不提倡使用,这部分代码将再后续的更新中去除,这里仅做了解就是了。

(1)使用uncaughtException捕捉进程异常

// 65行
process.on(‘uncaughtException‘, function (err) {
    // handle the error safely
    logger.fatal(‘System error‘, { message: err.message, stack: err.stack });
    process.emit(‘cleanup‘);
});

(2)使用domain模块捕获全局异常

// 96行
var d = require(‘domain‘).create();
d.on(‘error‘, function (err) {
    logger.fatal(‘Domain master‘, { message: err.message, stack: err.stack });
    process.exit(0);
});
d.run(function () {
...

另外,对各个模块,也使用了domain

// 415行
var d = require(‘domain‘).create();

d.on(‘error‘, function (err) {
scope.logger.fatal(‘domain ‘ + name, {message: err.message, stack: err.stack});
});
...

3.模块加载

这才是真正的重点,不过看过代码,发现一切都那么干净利略,也没有多层回调那些大坑,其实是用了async流程管理组件。

整体使用async.auto进行顺序调用;在加载modules时,又使用async.parallel,使其并行运作;当发生错误时,清理工作用到了async.eachSeries

下图是手工简单画的,说明了从代码103-438行之间,各模块的加载运行顺序。后面针对具体代码会使用UML图来代替。

下篇,我们也有必要对async组件进行详解梳理。这里,您只要能猜出代码意图,就不用太操心async的用法。下面,读读有关源码:

(1)初始网络

packages.json里看到使用了Express框架。通过前面部分的介绍,知道必须在入口程序里,初始化才对。具体如何调用的?下面的代码,显然十分熟悉。

Express是Nodejs重要的web开发框架,这里的网络network本质上就是以Express为基础的web应用,自然白皮书才会宣扬基于Http协议

// 215行
network: [‘config‘, function (cb, scope) {
    var express = require(‘express‘);
    var app = express();
    var server = require(‘http‘).createServer(app);
    var io = require(‘socket.io‘)(server);

    if (scope.config.ssl.enabled) {
        var privateKey = fs.readFileSync(scope.config.ssl.options.key);
        var certificate = fs.readFileSync(scope.config.ssl.options.cert);

        var https = require(‘https‘).createServer({
    ...

说明:这是async.auto常用的方法,network用到的任何需要回调的方法(这里是config),都放在这个数组里,最后的回调函数(function (cb, scope) {//code}),可以巧妙的调用,如: scope.config

这里的代码,仅仅初始化服务,没有做太多实质的事情,真正的动作在下面。

(2)构建链接

从下面的代码开始,我们才能看到这个应用的本质。270行代码用到了network等,如下:

// 270行
connect: [‘config‘, ‘public‘, ‘genesisblock‘, ‘logger‘, ‘build‘, ‘network‘, function (cb, scope) {

接着,下面的代码,加载了几个中间件,告诉我们,该应用接受ejs模板驱动的html文件。视图文件和图片、样式等静态文件都在public文件夹等,这些信息绝对比官方文档还有用。

// 277行
scope.network.app.engine(‘html‘, require(‘ejs‘).renderFile);
scope.network.app.use(require(‘express-domain-middleware‘));
scope.network.app.set(‘view engine‘, ‘ejs‘);
scope.network.app.set(‘views‘, path.join(__dirname, ‘public‘));
scope.network.app.use(scope.network.express.static(path.join(__dirname, ‘public‘)));
...

再下来,就是对请求参数和响应数据的处理,包括对节点peers中黑名单、白名单的过滤等,最后启动服务操作:

// 336行
scope.network.server.listen(scope.config.port, scope.config.address, function (err) {

(3)加载逻辑

看代码知道,其核心逻辑功能应该是:账户管理、交易和区块链。这些模块,有其执行顺序,我们需要在后面的篇章中,分别单独介绍。

// 379行
logic: [‘dbLite‘, ‘bus‘, ‘scheme‘, ‘genesisblock‘, function (cb, scope) {

    // 嵌套了async.auto
    async.auto({
        ...
        account: ["dbLite", "bus", "scheme", ‘genesisblock‘, function (cb, scope) {
            new Account(scope, cb);
        }],
        transaction: ["dbLite", "bus", "scheme", ‘genesisblock‘, "account", function (cb, scope) {
            new Transaction(scope, cb);
        }],
        block: ["dbLite", "bus", "scheme", ‘genesisblock‘, "account", "transaction", function (cb, scope) {
            new Block(scope, cb);
        }]
    }, cb);
    ...

(4)加载模块

上面所有代码的执行结果,都要被这里的各模块共享。下面的代码说明,各个模块都采用一致(不一定一样)的参数和处理方法,这样处理起来简单方便:

// 411行
modules: [‘network‘, ‘connect‘, ‘config‘, ‘logger‘, ‘bus‘, ‘sequence‘, ‘dbSequence‘, ‘balancesSequence‘, ‘dbLite‘, ‘logic‘, function (cb, scope) {

    // 对每个模块都使用`domain`监控其错误
    Object.keys(config.modules).forEach(function (name) {
        tasks[name] = function (cb) {
            var d = require(‘domain‘).create();

            d.on(‘error‘, function (err) {
                ...
            });

            d.run(function () {
                ...
            });
        }
    });

    // 让各个模块并行运行
    async.parallel(tasks, function (err, results) {
        cb(err, results);
    });

这里的模块既然都是并行处理,研究它们就不需要分先后了。

总结

这篇文章开始深入代码,但是仍然较为粗略。不过,对整个应用的基本架构已经了然。继续深入研究,方向路线也已然清晰。

代码中还有很多细节,我们并没有逐行介绍。个人认为,读代码就像看文章,先要概览,逐步深入,不一定一开始就逐字逐句去读,那样效率低、效果差。

对于这个app.js文件,成手读它可能就是分分钟的事情,而写出来却要罗嗦这么多。如果,你并没有觉得很轻松,甚至理解很困难,那么可能缺少对commanderdomainasync等组件或模块的了解,请看相关分享或官方文档。

链接

本系列文章即时更新,若要掌握最新内容,请关注下面的链接

本源文地址: https://github.com/imfly/bitcoin-on-nodejs

电子书阅读: http://bitcoin-on-nodejs.ebookchain.org

电子书下载: 下载页面 PDF文件 ePub文件 Mobi文件

时间: 2025-01-21 21:11:57

《Nodejs开发加密货币》之七:入口程序app.js解读的相关文章

《Nodejs开发加密货币》之十一:一张图熟练使用Async组件解决回调大坑

关于 <Nodejs开发加密货币>,是一个加密货币产品的详细开发文档,涉及到使用Nodejs开发产品的方方面面,从前端到后台.从服务器到客户端.从PC到移动.加密解密等各个环节.代码完全开源.文章免费分享. 相关资源见 http://ebookchain.org 前言 前面说过,在Nodejs的世界里"事事皆回调",学习使用Nodejs,最不可能回避的就是"回调"(用"调回"更直观些).无法回避,自然要积极面对,因此开源社区出现了很多

《Nodejs开发加密货币》之二十三:区块链

前言 亿书,是一款加密货币产品,用时髦的话说,更是一款实用的区块链产品.那么,区块链是什么?有那些特点?最近,以太坊硬分叉事件给了我们很多启示,能不能彻底杜绝区块链分叉行为?这一章,我们通过认真阅读和理解亿书相关的代码逻辑,来详细解释和说明这些问题,以便更加深入的了解和学习这项技术. 源码 blocks.js https://github.com/Ebookcoin/ebookcoin/blob/v0.1.3/modules/blocks.js block.js https://github.c

《Nodejs开发加密货币》之二十七:开发通用的HTML组件

人的懒惰常常是麻烦的开始.多数程序员都希望自己的工作一劳永逸,一次开发,到处使用,成了人人追逐的目标,我也不例外.最初写<Nodejs开发加密货币>系列文章,因为不喜欢设定好了去写,所以目录反复修改,索性弄了小工具gitbook-summary:在写入门文章的时候,反复搜索github,索性把检索与制图集成到一起,弄了个开发语言检索统计工具(见<Node.js让您的前端开发像子弹飞一样>文章实例):阅读源码的时候,手动整理Uml图很辛苦,干脆写成了js2uml工具(见<轻松从

《Nodejs开发加密货币》之二十一:交易

题外话:这篇文章,耗费了我大量精力,用UML表达javascript类及流程本来就不是什么容易的事情,用来描述加密货币交易这种验证逻辑非常多的代码更难,加之Nodejs的回调在这些代码里嵌套很深,所以如何把异步调用变成人类容易理解的顺序调用,也做了一番取舍,时间不知不觉就过了一星期. 所幸,赶在比特币减半的今天完成并发布这篇文章,也算在区块链火热的今天,<Nodejs开发加密货币>走到了一个关键节点:触及了加密货币的灵魂和腹地.动辄几千一枚的比特币等加密货币可能会消亡,但是背后的技术却蓬勃发展

《Nodejs开发加密货币》之八:一个精巧的p2p网络实现

发布本文时,比特币价格 ¥2909.92 / $448.29 .为什么一个凭空设计出来的加密货币如此受追捧?为什么微软.IBM等巨头纷纷进入?为什么尝试了解比特币的技术人员,都会被深深吸引?它到底有什么诱人之处?<Nodejs开发加密货币>,让我们一起探索其背后的密码. <Nodejs开发加密货币>,目的是提供加密货币(亿书币)的详尽开发文档,涉及到使用Nodejs开发产品的方方面面,从前端到后台.从服务器到客户端.从PC到移动.从IO密集型到计算密集型.从中心化到去中心化.加密解

《Nodejs开发加密货币》之二十六:轻松从Js文件生成UML类图

前言 上一篇<函数式编程入门经典>,罗嗦了很长,很多小伙伴看得云里雾里.这里提供一个实例,让大家切身感受函数式编程的奥妙和趣味.当然,仅仅为了举例而写代码就没有什么意义了,本书提供的例子都是承担了某项任务的具体项目或工具,这个例子自然也不能例外. 本书用到了大量的Uml类图,经常有小伙伴问我用什么工具画的.说实话,前几篇是我个人一点点手工整理的,但后来就感觉在浪费生命,作为程序员,怎么可能容忍这样的事情反复发生.所以,就有了 js2uml(见参考)这个小工具.只不过,当初目的单一,仅仅使用正则

《Nodejs开发加密货币》之十五:加密货币就是货币

这是一篇加密货币的入门文章,是对<Nodejs开发加密货币>的入门指南部分的补充,主要写给那些没有接触过比特币.加密货币的小伙伴,接下来的内容,都将与加密货币相关. 前言 "加密货币就是货币"听起来挺"白痴的".想想背后的意思,言外之意就是"加密货币可能不是货币",就非常值得玩味了.事实上,在我接触的很多朋友当中,一开始认为后者的更多.包括我自己,也是经过探究一段时间之后,才认定这个结论的. 惯性定律不仅存在于物质世界,也存在于人类的

《Nodejs开发加密货币》之十二:静态网站开发全景扫描

前言 在前面的入门部分,介绍了Nodejs在前端开发中的应用,并通过具体项目说明了Nodejs在比特币客户端领域被广泛应用.当时为了介绍Nodejs入门技术,一切都是从头创建,没有引入前端框架.但在具体的项目实践中,前端是有框架可以选择的,效率和体验会有明显提升. 具体到前端框架,我的选择是Ember.js.Ember给开发带来一种飞一般的感觉,如果问前端框架哪家强,我会毫不犹豫的说Ember.(具体为什么,网上仍然争论不休,本文不做讨论) 本文重点介绍静态网站的类型,亿书官网的技术选型,以及在

《Nodejs开发加密货币》之十六:利益,魔鬼与天使的共同目标

前言 上篇文章<加密货币就是货币>吸引了很多小伙伴关注,非常感谢.同时说明,很多小伙伴因为对加密货币不了解(或者有误解),所以才会敬而远之,错失良机. 这篇文章,继续上一篇,仍然通过直白的语言来讲解技术.涉及到的内容包括未来趋势,应用场景和风险提示,让我们更直观地理解币圈里的一些概念,比如:智能合约等. 利益,主宰着人类行为 人活着到底是为了什么?我们每个人可能都问过自己这个问题.我们有时候踌躇满志,想要拥有一切.有时候又高尚地低下头,崇尚与世无争,无忧无虑.但在纷繁复杂的真实世界里,我们总会