Redis学习——安装

一、Redis下载官方网址:https://redis.io/二、Redis环境安装Redis需要C语言编译环境[root@localhost redis-3.2.9]# yum install gcc-c++ -y三、Redis安装第一步:将redis的源码包上传到linux系统。第二步:解压缩redis的源码包。输入命令:tar -zxvf redis压缩包第三步:进行编译。 cd到解压后的目录 输入命令:make第四步:进行安装。输入命令:make install PREFIX=/usr/local/redisPREFIX 必须是大写的。 第五步:将redis解压文件夹中的redis.conf复制到 /usr/local/redis。输入命令:cp redis.conf /usr/local/redis/

January 20, 2019 · 1 min · jiezi

如何利用数据架构带动企业增长?

对于架构师而言,技术的发展是无尽的,在搭建和实践智能数据架构的过程中,架构师们都会或多或少地遇到一些疑惑和挑战,如何解决在架构建设中遇到的某些问题?架构建设的领域又有什么新的行业动态和技术方法?近日,在个推TechDay全国沙龙北京站的现场,几位资深架构师围绕“以智能数据架构,挖掘增长金矿”的主题,开启了对智能数据技术的深入探讨。贝壳金服2B2C CTO史海峰 《架构之十年磨剑,大巧不工》在传统的IT行业中,企业级业务系统是技术水平的高峰,比如电信、金融、税务系统。以电信行业为例,它有其独有的行业特征:业务系统本身就是生产系统,信息化程度高;24小时全天候跨地域不间断地提供服务;业务复杂,功能多样等。电信行业中以中国移动为代表的架构设计规划最为领先,该架构有一套完整的设计规范,整体建设周期也很长。而在PC互联网时代,电子商务纷纷崛起,很多互联网电商平台都面临着从自营到平台化转变的挑战。自营与平台化最大的区别在于,自营只有一个商户,而真正的平台则需要像云计算一样,支持多商户的入驻与经营。对于架构师来说,要实现这样的平台转型,需要有清晰的业务系统架构总图、系统架构蓝图以及技术架构规划。进入到移动互联网时代,手机的普及使得O2O快速发展。O2O平台对于系统稳定性的要求非常高,而且对业务发展的响应速度也提出了新的 ,其所需要的技术架构非常复杂、需要有严谨的基础架构和运维机制,还要维持创新能力。到了产业互联网时代,架构师应该对行业有更深入的理解和实践,同时,也要有相应的架构思维,从“点、线、面、体”各维度提升认知,在进行架构建设时,架构师不仅要考虑到技术上实现的可能性,也要考虑到行业特点,以及企业各个方面对于互联网技术的态度与需求。个推平台架构主管王志豪 《微服务网关架构实践》微服务是指,将单一的应用程序拆分成多个微小的服务,各个小服务之间低耦合,高内聚,每个小的服务可以单独进行开发,不依赖于具体的编程语言,也可以使用不同的数据存储技术,各个服务可以独立部署,拥有各自的进程,相互之间通过轻量化的机制进行通信,所有的服务共同实现具体的业务功能。个推整体的微服务架构,主要是基于Docker和Kubernetes进行实践的。个推将应用服务分为三层,最上层是API网关,为服务提供统一的入口;第二层是业务逻辑层,主要实现具体的业务逻辑;最底层是基础服务层,为同一产品线下的不同产品提供共同的基础服务。在构建整个微服务体系时,个推选择了研发自己的网关。目前市面也有很多优秀的网关产品,但都并不是特别适合个推的微服务体系。第一,个推的配置和服务注册与发现均基于Consul实现的;第二,很多的开源的网关的配置相对于个推的需求略显复杂;第三,由于不同的功能很难用统一的网关去实现,个推更希望在API网关的功能扩展上保持足够的灵活性;第四,个推的微服务体系是基于Docker和Kubernetes进行实践的,其他的网关较难直接融入个推的微服务体系。个推选取了OpenResty+Lua作为实现网关的技术选型。其中OpenResty是一个基于 Nginx 与 Lua 的高性能 Web 平台,Lua则是一个较为轻量的、扩展性较强的语言。同时,个推也借鉴了Kong和Orange的插件机制,通过插件的方式实现网关功能的扩展。个推的微服务网关的设计重点,体现在插件的设计和请求过程,每个插件都会在OpenResty的一个或多个阶段起作用。在进行请求处理的时候,个推会按照产品配置和插件规则筛选出具体执行的插件,然后实例化插件,对流量进行处理。个推微服务网关还有规则配置简单,不同插件实现灵活,配置基于Conusl实时热更新等特点。在自己的微服务网关中,个推还实现了诸如动态路由、流量控制、Auth鉴权、链路追踪、A/B Testing等功能。在实践微服务网关的过程中,个推还有一些有待改进的地方,比如:网关的弹性设计不够、网关还需要进一步与DevOps进行结合等。百度主任架构师郑然《搜索引擎的大数据计算架构》搜索引擎的建设主要分为三个方面,第一,是内容抓取部分,爬虫会将海量信息抓取下来;第二,是检索系统部分,系统需要加入对已抓取信息的内容的理解,放到索引中;第三,是索引构建部分。支持搜索引擎计算的建库部分中,有一个非常核心的系统:Tera,它是一个大型分布式表格存储系统,可以进行高性能、可伸缩的半结构化存储,同时支持存储万亿量级的超链和网页信息。Tera系统有八个核心技术:1、数据模型的全局有序;2、实时的读写和区间扫描(这一点与数据模型的全局有序也密不可分);3、可以支持行式存储和列式存储;4、友好的分布式文件系统;5、利用SSD cache热数据;6、数据压缩,异步IO和分组提交等性能优化手段;7、支持秒级分裂合并,并且能够实现自动负载均衡;8、在分布式数据库上实现了分布式事务。Tera是百度搜索引擎从批量处理迈向实时流式计算的最基础的架构,它可以提供实时的读写能力,同时提供海量存储和增量计算,并且节约增量成本。在研发方面,Tare也能够使中间数据可见、Debug能力增强。本质上来说,Tera是一个分布式存储系统,它需要遵循分布式存储系统的设计要素,包括:明确数据模型、存储引擎的设计、数据分片的方式、如何管理元数据、高可用的设计以及应用的是分层式还是竖井式的存储。同时,存储系统也需要进行性能优化工程的实践,包括指标数据的采集和可视化、先做profiling再手动优化、面向SSD进行编程、Batch&pipeline&asynchronous和学习存储引擎的先进研究成果。京东数科高级DBA潘娟《Sharding-Sphere云架构演化》互联网应用业务的特点是用户和数据量大、产品迭代迅速、业务组合复杂、突发性流量暴增以及7*24小时不间断提供服务,这些特点导致互联网架构从一开始的单体式架构发展到分布式微服务,再到云原生架构。分布式微服务使得系统解耦可用性得到提升,而云原生架构更好地实现了资源按需伸缩、自动化的部署和管理。同时,互联网数据库也在不断地升级,由于早期的RDBMS无法满足数据扩展的需求,NoSQL应运而生。再到后来,囊括分布式数据库、分布式数据库中间件以及云数据库的NewSQL也都纷纷顺应数据扩展的需求而产生。作为分布式数据库的中间件,Sharding-Sphere可以借助底层成熟的关系型数据库进行增量持续的开发,这满足了分布式的需求,并且能够大大降低运维和接入成本。同时,Sharding-Sphere也应该拥有四种核心能力:1、能够通过数据拆分或读写分离,实现数据分片的能力。具体来讲,数据分片需要先通过SQL解析,对查询语句进行优化、合并和改写,再找到可以执行的SQL,最终实现结果归并。2、分布式事务能够保证数据的一致性。在数据还未打散之前,我们可以用传统的ACID(Atomicity、Consistency、Isolation、Durability)保障数据的一致性;但在数据被打散之后,我们则需要引进新的BASE原则(Basically Available、Soft state、Eventual consistency)来保障数据一致。有两种方式可以实现分布式事务BASE:一种为XA,虽然XA支持回滚,能够达到强一致性,但当并发量变大,它的性能会急剧下滑;而另一种方式是柔性事务,在这种方式下,当业务量急剧上升时,并发性能只会发生略微衰退,并且能够保证,在最终达到一致。3、数据库治理。Sharding-Sphere可以通过注册中心的操作,实现统一管理、熔断或失效转移的功能,同时可以通过拓扑图和调用链来打造APM监控。4、弹性伸缩。Sharding-Sphere是由 Sharding-JDBC、Sharding-Proxy 和 Sharding-Sidecar这3款相互独立的产品组成的,他们均提供标准化的数据分片、分布式事务和数据库治理功能。Sharding-JDBC更适用于单体应用和分布式微服务,它是一个轻量级的Java框架;Sharding-Proxy实现了MySQL的二进制协议,并且不存储任何数据;Sharding-Sidecar的核心是Service Mesh Sidecar,能够帮助本地的应用层与网络节点进行沟通。

January 16, 2019 · 1 min · jiezi

小程序云开发实战系列02--云数据库

以前一直是使用关系型数据库,第一次使用NoSQL,跟大家分享一下我有限的使用心得,希望对像我一样初使用NoSQL的开发者有所帮助。首先说说微信小程序云开发里集成的这个NoSQL,官方并没有说明是哪种NoSQL数据库,但从开发文档和暴露的API,还有官方论坛里的讨论来看应该是一个简化版的MongoDB。需要指出的是微信小程序关于云数据库的开发文档非常的简略,对于像我这样没有太多NoSQL经验的用户,很多时候需要参考MongoDB的相关文档。接下来重点谈谈我在使用这个NoSQL云数据库时最不适应的一个痛点—-文档级别的原子操作。我们经常要使用到原子操作,来避免当多个用户同时对同一个field(字段)编辑时发生冲突。我在使用前其实最担心的痛点是有无schema的区别,但是使用下来发现我挺习惯,也挺喜欢无schema的,后文再详说。现在具体来看看MongoDB只支持document(文档)级别的原子操作。对于我来说,这个限制鼓励我尽量把所有关系都放在一个document里。对此我一开始是有点抗拒的,对于从关系型数据库过来的人特别不习惯。而更让我苦恼的是微信小程序云开发集成的这个云数据库是一个简化版MongoDB,只提供了非常有限的原子操作指令(command)。对于一些常用的document级别原子操作,我必须构想自己的解决办法,而没有提供直接对应的command。以下是两个我在实际开发中遇到的这类问题及我的解决办法:1.应用场景:对于一个视频,我需要一个叫total_likes的field(字段),当有用户点击“喜欢”时该field递增1,当有用户取消“喜欢”时该field递减1。痛点:小程序云数据库只提供了递增指令的原子操作,没有提供递减指令。const _ = db.commanddb.collection(‘video’).doc(‘video-id’).update({ data: { total_likes: _.inc(1) }})解决办法:要实现递减的原子操作,只需在递增指令里传入负数,如data: { total_likes: _.inc(-1)}2.应用场景:对于一个线上课程,我需要一个叫subscribers的field(字段)来记录有多少人订阅了该课程。当有用户点击“订阅”时该字段需记录该用户的id,名字及头像;当有用户取消“订阅”时需把该用户从subscribers字段里删除。痛点:我们很自然的会想到用数组(Array)数据类型来维护subscribers这个字段,虽然小程序云数据库提供了一些针对数组的原子操作,如push,pop,shift和unshfit,可是无法实现取消订阅这个场景的原子操作。解决办法:弃用Array转而使用对象(object)数据类型来维护subscribers这个字段。最终的数据看起来会是这样的:{ “subscribers”: { “userID-1”: { “name”: “小明”, “avatar”: “https://avatar-1.com” }, “userID-2”: { “name”: “小红”, “avatar”: “https://avatar-2.com” }, “userID-3”: { “name”: “小李”, “avatar”: “https://avatar-3.com” }, … }}当有用户订阅时的原子操作:const subscriber = “subscribers.” + user.id;db.collection(‘class’).where({ _id: ‘classID’,}).limit(1).update({ data: { [subscriber]: { avatar: user.avatar, name: user.name, } }})当有用户取消订阅时的原子操作:const subscriber = “subscribers.” + user.id;db.collection(‘class’).doc(‘classID’).update({ data: { [subscriber]: _.remove() }})前文说到我很喜欢无schema,因为它非常适合快速迭代开发。而且由于云数据库使用的是类似JSON的数据结构,对于全栈开发者,基本上可以实现由前端来定义数据结构。这样的开发流程非常适合小团队,不需要庞大的并行开发,突出沟通效率和对产品需求的随机应变。顺带一提的是微信小程序云开发能力是从基础库2.2.3开始支持的,但如果要支持所有版本的基础库,可以在 app.json / game.json 中增加字段 “cloud”: true本系列第一章:小程序云开发实战系列01–云环境设置《Meetup丨活动报名组局》是我最近开发的一个活动报名预约工具小程序,这个系列文章主要来自我在开发这款小程序时的一些体会心得。感兴趣的小伙伴可以扫下面的二维码进入我的小程序。 ...

January 6, 2019 · 1 min · jiezi

2018年阿里云NoSQL数据库大事盘点

NoSQL一词最早出现在1998年。2009年Last.fm的Johan Oskarsson发起了一次关于分布式开源数据库的讨论,来自Rackspace的Eric Evans再次提出了NoSQL概念,这时的NoSQL主要是指非关系型、分布式、不提供数据库设计模式。从2009年开始,NoSQL开始逐渐兴起和发展。回望历史应该说NoSQL数据库的兴起,完全是十年来伴随互联网技术,大数据数据的兴起和发展,NoSQL在面临大数据场景下相对于关系型数据库运用,这一概念无疑是一种全新思维的注入。正值2018年底之际,让我们来看看NoSQL数据库领域发生了哪些深刻的变化和阿里云NoSQL的关键技术,尝试探究未来十年云计算时代新趋势。云NoSQL数据库成为数据库领域重要增长引擎云化趋势不可避免,根据Gartner的报告,2017年超过73%的DBMS增长来自云厂商,Gartner象限里面AWS在领导者象限上升趋势明确,老牌厂商下滑严重。在2018年Gartner报告中,阿里云数据库更是中国唯一首次入围远见者象限。而在众多云厂商里面增长最快的又是NoSQL数据库,云NoSQL成为数据库领域重要增长引擎。NoSQL融合和跨界创新成为新趋势2018年对SQL/NoSQL是一个重要的一年。MySQL 8.0重点增强了json支持,整行是一个文档,并提供了专门的接口用于访问,类似MongoDB的能力。MongoDB release了4.0版本,NoSQL领域里面第一个开始支持事务,在刚刚举行的2018年的AWS:reivent大会上,dynamodb也宣布支持事务。传统SQL/NoSQL不再界限分明,NoSQL和SQL数据库融合和跨界创新逐渐成为新趋势,事务不再是关系型数据库独门秘籍。多模成为事实标准如果去年Gartner在2017年数据库厂商推荐报告中还在谈多模是发展趋势的话,今年再看看各大数据库厂商都已经纷纷支持了多模,如Azure的cosmosDB早就支持K/V,列簇,文档,图。AWS的DynamoDB支持KV和文档,阿里云新发布X-Pack更是将多模推上新高度,KV的基础上,同时支持时序、时空、图、文档等多种数据模型。全球分布式成为重量级玩家的入门门槛云厂商之所以有这么强大的活力,除了在数据库内核本身长足的进步之外,结合云服务整体生态的创新是重要的一环。云厂商3A玩家的主流NoSQL数据库都已经全面覆盖了全球分布式能力,助力企业参与全球业务竞争。是否具备全球扩展和分布式的能力,是云NoSQL数据库重要的入门门槛。阿里云集团是国内最早提出数据战略,本身也拥有最大体量的数据,是最早投入NoSQL数据库技术研发,目前也拥有国内最大的专家团队。在持续十年技术加持下,阿里云NoSQL目前覆盖了所有主流的NoSQL数据库,如Redis/mongodb/HBase等等。阿里云NoSQL数据库多项独家关键技术领先竞争对手,是国内当之无愧的NoSQL数据库排头兵。关键技术一:8月份推出Redis独家全球多活8月份阿里云Redis,全球云厂商首家推出Redis全球多活,解决多媒体、游戏、电商等多行业客户业务出海、地域级灾备、全球同服/多活、跨域数据迁移的诉求。多项关键能力独步领域内:全球多活:内核级别支持,实现多节点全球容灾。高可用:同步通道支持断点续传,容忍天级别的隔断,子实例HA自动切换高性能:单通道10万TPS,高于Redis处理速度;延迟低、洲际内百毫秒延迟支持数据最终一致性:CRDT冲突解决方案+数据一致性检测,基于Redis内核改造,原生Redis内核无此功能。关键技术二:10月份全球第一家云厂商支持MongoDB 4.0版本,多项独家企业级能力,领域内领先跨域双活:数据双向同步,相比现有通道产品提升100%效率,业界领先。首创物理+逻辑备份双能力:物理备份,相比开源版本备份效率提升100%,恢复效率提升300%。创新性提供逻辑snapshot能力,解决政企等定期大批量数据更新需求,同架构下性价比提升100%。秒级监控及智能诊断能力:提供每秒粒度的监控数据采集,监控精度提升数十倍。结合监控、审计等数据提供智能诊断,分析系统运行瓶颈并自动化提供优化建议。关键技术三:HBase发布全新X-Pack,支持多模和融合负载能力,定义云HBase业界新标准12月13日第8届中国云计算标准和应用大会隆重发布X-Pack,新增多模型和融合负载支持。多模型支持:同时支持KV、时序、时空、图、文档等多种数据模型,内置丰富处理能力,让业务开发效率提升百倍。融合负载支持:ApsaraDB HBase在在线能力的基础上,融合流处理、批处理、OLAP,OLTP、高速对象存储,全文检索等能力,提供客户业务开箱即用的能力。多模型和融合负载是云HBase领域第一个具备此能力的云服务。阿里云6月6日全球首发2.0,12.13日又全新推出X-Pack,持续领先的背后是高强度,持续的研发投入,阿里巴巴集团早在2010开始研究并把HBase投入生产环境使用,从最初的淘宝历史交易记录,到蚂蚁安全风控数据存储。持续8年的投入,历经8年双十一锻炼。4个PMC,6个committer,造就了国内最大最专业的HBase技术团队,其中HBase内核中超过200+重要的feature是阿里贡献。集团内部超过万台的规模,单集群超过千台,全球领先。云HBase的能力也得到了客户的高度认可,短短1年间,就覆盖了社交、金融、政企、车联网、交通、物流、零售、电商等数十个个行业,帮单用户顶住千万级QPS的业务压力,以及百PB级数据高效存储和处理。站在2018/2019交替之际,展望未来,阿里云NoSQL数据库也会持续围绕客户业务,成本,运维各个方面进行优化和创新,成就客户,争做最好的NoSQL数据库而持续努力!本文作者:所在jason阅读原文本文为云栖社区原创内容,未经允许不得转载。

December 25, 2018 · 1 min · jiezi

Node.js和NoSQL开发比特币加密货币应用程序(上)

我一直在跟踪比特币之类的加密货币相关主题有几个月了,我对所发生的一切都非常着迷。作为一名Web应用程序开发人员,我一直特别感兴趣的一个主题是加密货币交易以及如何制作它们。从前端看,这些应用程序似乎是用于管理帐户,将比特币转换为美元等法定货币以及将比特币转账给其他人的工具,但它们能做更多吗?我们将看一些Node.js和NoSQL数据库Couchbase的例子,它们涵盖了以加密货币交易为模型的主题。免责声明:我不是加密货币专家,也没有参与金融服务或交易所的任何开发。我是这个主题的狂热爱好者,从本文中获得的任何内容都应该经过适当的测试和使用,风险自负。the take-Away你将从这篇特定文章中获得那些内容,将无法获得那些内容呢?让我们从你不会从本文中得到的东西开始:我们不会配置任何银行或信用卡服务来交易美元等法定货币。我们不会将任何已签名的交易广播到比特币网络,最终确定转账。也就是说,以下是你可以期待在本文中学习的一些内容:我们将创建一个分层确定性(HD,hierarchical deterministic)钱包,它可以为给定的种子生成无限量的密钥,每个密钥代表一个用户钱包。我们将根据主种子创建每个包含钱包的用户帐户。我们将创建代表交易所存款,取款和资金转账的交易,而不实际使用法定货币。我们将从比特币网络中查找余额。我们将创建在比特币网络上广播的签名交易。我们将在本文中看到许多可以更好地完成的事情。如果你发现了可以改进的内容,请务必在评论中分享。就像我说的那样,我不是这个主题的专家,只是一个粉丝。项目要求为了成功完成这个项目,必须满足一些要求:你必须安装并配置Node.js 6+。你必须安装Couchbase 5.1+并配置Bucket和RBAC配置文件。重点是我将不会介绍如何启动和运行Couchbase。这不是一个困难的过程,但是你需要一个Bucket设置一个应用程序帐户和一个用N1QL查询索引。创建具有依赖关系的Node.js应用程序在开始添加任何逻辑之前,我们将创建一个新的Node.js应用程序并下载依赖项。在计算机上的某个位置创建项目目录,并从该目录中的CLI执行以下命令:npm init -ynpm install couchbase –savenpm install express –savenpm install body-parser –savenpm install joi –savenpm install request request-promise –savenpm install uuid –savenpm install bitcore-lib –savenpm install bitcore-mnemonic –save我知道我可以在一行中完成所有的依赖安装,但我想让它们清楚地阅读。那么我们在上面的命令中做了什么?首先,我们通过创建package.json文件来初始化一个新的Node.js项目。然后我们下载我们的依赖项并通过–save标志将它们添加到package.json文件中。对于此示例,我们将使用Express Framework。express,body-parser和joi包都与接受和验证请求数据相关。因为我们将与公共比特币节点进行通信,所以我们将使用request和request-promise包。非常受欢迎的bitcore-lib软件包将允许我们创建钱包并签署交易,而bitcore-mnemonic软件包将允许我们生成可用于我们的HD钱包密钥的种子。最后,couchbase和uuid将用于处理我们的数据库。现在我们可能想要更好地构建我们的项目。在项目目录中添加以下目录和文件(如果它们尚不存在):package.jsonconfig.jsonapp.jsroutes account.js transaction.js utility.jsclasses helper.js我们所有的API端点都将分为几类,并放在每个适当的路由文件中。我们不必这样做,但为了使我们的项目更干净一点。我们去删除大量的比特币和数据库逻辑,我们将把所有非数据验证的内容添加到我们的classes/helper.js文件中。config.json文件将包含我们所有的数据库信息以及我们的助记符种子。在一个现实的场景中,这个文件应该被视为黄金般重要,并获得尽可能多的保护。app.js文件将具有我们所有的配置和引导逻辑,用于连接我们的路由,连接到数据库等。为方便起见,我们将为项目添加一个依赖项并进行设置:npm install nodemon –save-devnodemon包将允许我们每次更改文件时热重新加载项目。这不是一个必须的要求,但可以为我们节省一些时间。打开package.json文件并添加以下脚本以实现它:…“scripts”: { “test”: “echo "Error: no test specified" && exit 1”, “start”: “./node_modules/nodemon/bin/nodemon.js app.js”},…我们可以在此时开始我们的应用程序的开发过程。开发数据库和比特币逻辑在开发我们的应用程序时,在我们开始担心API端点之前,我们想要创建我们的数据库和比特币相关的逻辑。我们将把时间花在项目的classes/helper.js文件中。打开它并包含以下内容:const Couchbase = require(“couchbase”);const Request = require(“request-promise”);const UUID = require(“uuid”);const Bitcore = require(“bitcore-lib”);class Helper { constructor(host, bucket, username, password, seed) { this.cluster = new Couchbase.Cluster(“couchbase://” + host); this.cluster.authenticate(username, password); this.bucket = this.cluster.openBucket(bucket); this.master = seed; } createKeyPair(account) { } getWalletBalance(addresses) { } getAddressBalance(address) { } getAddressUtxo(address) { } insert(data, id = UUID.v4()) { } createAccount(data) { } addAddress(account) { } getAccountBalance(account) { } getMasterAddresses() { } getMasterKeyPairs() { } getMasterAddressWithMinimum(addresses, amount) { } getMasterChangeAddress() { } getAddresses(account) { } getPrivateKeyFromAddress(account, address) { } createTransactionFromAccount(account, source, destination, amount) { } createTransactionFromMaster(account, destination, amount) { }}module.exports = Helper;我们将把这个类作为我们应用程序的singleton来发送。在constructor方法中,我们建立与数据库集群的连接,打开Bucket并进行身份验证。打开的Bucket将在整个helper类中使用。让我们在完成数据库逻辑之前跳出比特币逻辑。如果你不熟悉HD钱包,它们本质上是一个由单个种子衍生而来的钱包。使用种子,你可以得到children,那些children可以再有children,等等。createKeyPair(account) { var account = this.master.deriveChild(account); var key = account.deriveChild(Math.random() * 10000 + 1); return { “secret”: key.privateKey.toWIF().toString(), “address”: key.privateKey.toAddress().toString() }}createKeyPair函数中的master变量表示顶级种子密钥。每个用户帐户都是该密钥的直接子项,因此我们根据account值派生子项。account值是人员编号,创建的每个帐户都将获得增量编号。但是,我们不会生成帐户密钥并将其称为一天。相反,每个帐户密钥将有10,000个可能的私钥和公钥,以防他们不想多次使用同一个密钥。一旦我们随机生成了一个密钥,我们就会返回它。同样,我们有一个getMasterChangeAddress函数,如下所示:getMasterChangeAddress() { var account = this.master.deriveChild(0); var key = account.deriveChild(Math.random() * 10 + 1); return { “secret”: key.privateKey.toWIF().toString(), “address”: key.privateKey.toAddress().toString() }}当我们开始创建帐户时,它们将从一开始,为交易或Web应用程序留下零,或者你想要调用它。我们还为此帐户分配了10个可能的地址。这些地址将做两件事。第一个是他们将比特币发送到其他账户,第二个是他们将收到剩余款项,也就是所谓的变更。请记住,在比特币交易中,必须花费所有未花费的交易输出(UTXO),即使它小于期望的金额。这意味着所需的金额将被发送到目的地,剩余部分将被发送回这10个地址中的一个。还有其他方法或更好的方法吗?当然,但这个将适用于这个例子。为了获得我们使用或使用HD种子生成的任何地址的余额,我们可以使用公共比特币资源管理器:getAddressBalance(address) { return Request(“https://insight.bitpay.com/api/addr/" + address);}上面的函数将采用一个地址并以十进制格式和satoshis获得余额。展望未来,satoshi价值是我们唯一的相关价值。如果我们有给定帐户的X个地址,我们可以使用如下函数获得总余额:getWalletBalance(addresses) { var promises = []; for(var i = 0; i < addresses.length; i++) { promises.push(Request(“https://insight.bitpay.com/api/addr/" + addresses[i])); } return Promise.all(promises).then(result => { var balance = result.reduce((a, b) => a + JSON.parse(b).balanceSat, 0); return new Promise((resolve, reject) => { resolve({ “balance”: balance }); }); });}在上面的getWalletBalance函数中,我们正在为每个地址发出请求,当它们全部完成时,我们可以添加余额并返回它们。能够传输加密货币需要的不仅仅是地址余额。相反,我们需要知道给定地址的未花费的交易输出(UTXO)。这可以使用BitPay中的相同API找到:getAddressUtxo(address) { return Request(“https://insight.bitpay.com/api/addr/" + address + “/utxo”).then(utxo => { return new Promise((resolve, reject) => { if(JSON.parse(utxo).length == 0) { reject({ “message”: “There are no unspent transactions available.” }); } resolve(JSON.parse(utxo)); }); });}如果没有未使用的交易输出,则意味着我们无法传输任何内容,而是应该抛出错误。足够的发送代表的是一个不同的意思。例如,我们可以这样做:getMasterAddressWithMinimum(addresses, amount) { var promises = []; for(var i = 0; i < addresses.length; i++) { promises.push(Request(“https://insight.bitpay.com/api/addr/" + addresses[i])); } return Promise.all(promises).then(result => { for(var i = 0; i < result.length; i++) { if(result[i].balanceSat >= amount) { return resolve({ “address”: result[i].addrStr }); } } reject({ “message”: “Not enough funds in exchange” }); });}在上面的函数中,我们将获取一个地址列表并检查哪个地址的数量大于我们提供的阈值。如果他们都没有足够的余额,我们应该发送这个消息。最终的实用程序相关功能,我们已经看到了一些:getMasterKeyPairs() { var keypairs = []; var key; var account = this.master.deriveChild(0); for(var i = 1; i <= 10; i++) { key = account.deriveChild(i); keypairs.push({ “secret”: key.privateKey.toWIF().toString(), “address”: key.privateKey.toAddress().toString() }); } return keypairs;}上面的函数将为我们提供所有主密钥,这对于签名和检查值非常有用。重申一下,我使用有限值来生成多少个键。你可能会也可能不想这样做,这取决于你。现在让我们深入研究一些用于存储应用程序数据的NoSQL逻辑。截至目前,我们的数据库中没有数据。第一个逻辑步骤可能是创建一些数据。虽然独立并不是特别困难,但我们可以创建这样的函数:insert(data, id = UUID.v4()) { return new Promise((resolve, reject) => { this.bucket.insert(id, data, (error, result) => { if(error) { reject({ “code”: error.code, “message”: error.message }); } data.id = id; resolve(data); }); });}基本上,我们接受一个对象和一个id用作文档密钥。如果未提供文档密钥,我们将自动生成它。完成所有操作后,我们将返回创建的内容,包括响应中的id。所以我们假设我们要创建一个用户帐户。我们可以做到以下几点:createAccount(data) { return new Promise((resolve, reject) => { this.bucket.counter(“accounts::total”, 1, { “initial”: 1 }, (error, result) => { if(error) { reject({ “code”: error.code, “message”: error.message }); } data.account = result.value; this.insert(data).then(result => { resolve(result); }, error => { reject(error); }); }); });}请记住,帐户由此示例的自动递增数值驱动。我们可以使用Couchbase中的counter创建递增值。如果计数器不存在,我们将其初始化为1并在每次下一次调用时递增。请记住,0是为应用程序密钥保留的。在我们得到计数器值之后,我们将它添加到传递的对象并调用我们的insert函数,在这种情况下为我们生成一个唯一的id。我们还没有看到它,因为我们没有任何端点,但我们假设在创建帐户时,它没有地址信息,只有帐户标识符。我们可能想为用户添加地址:addAddress(account) { return new Promise((resolve, reject) => { this.bucket.get(account, (error, result) => { if(error) { reject({ “code”: error.code, “message”: error.message }); } var keypair = this.createKeyPair(result.value.account); this.bucket.mutateIn(account).arrayAppend(“addresses”, keypair, true).execute((error, result) => { if(error) { reject({ “code”: error.code, “message”: error.message }); } resolve({ “address”: keypair.address }); }); }); });}添加地址时,我们首先按文档ID获取用户。检索文档后,我们获取数字帐户值并创建10,000个选项的新密钥对。使用子文档操作,我们可以将密钥对添加到用户文档,而无需下载文档或对其进行操作。关于我们刚刚做了什么非常严肃的事情。我将未加密的私钥和公共地址存储在用户文档中。这对生产来说是一个很大的禁忌。还记得你读过的所有关于人们钥匙被盗的地方的故事吗?实际上,我们希望在插入数据之前加密数据。我们可以通过使用Node.js加密库来实现,或者如果我们使用Couchbase Server 5.5,Couchbase的Node.js SDK会提供加密。我们不会在这里探讨它。好的,我们现在已经在数据库中获得了帐户数据和地址。让我们查询该数据:getAddresses(account) { var statement, params; if(account) { statement = “SELECT VALUE addresses.address FROM " + this.bucket._name + " AS account USE KEYS $id UNNEST account.addresses as addresses”; params = { “id”: account }; } else { statement = “SELECT VALUE addresses.address FROM " + this.bucket._name + " AS account UNNEST account.addresses as addresses WHERE account.type = ‘account’”; } var query = Couchbase.N1qlQuery.fromString(statement); return new Promise((resolve, reject) => { this.bucket.query(query, params, (error, result) => { if(error) { reject({ “code”: error.code, “message”: error.message }); } resolve(result); }); });}上面的getAddresses函数可以做两件事之一。如果提供了帐户,我们将使用N1QL查询来获取该特定帐户的所有地址。如果未提供帐户,我们将获取数据库中每个帐户的所有地址。在这两种情况下,我们只获取公共地址,没有任何敏感信息。使用参数化的N1QL查询,我们可以将数据库结果返回给客户端。我们的查询中需要注意的事项。我们将地址存储在用户文档中的数组中。使用UNNEST运算符,我们可以展平这些地址并使响应更具吸引力。现在假设我们有一个地址,我们想获得相应的私钥。 我们可能会做以下事情:getPrivateKeyFromAddress(account, address) { var statement = “SELECT VALUE keypairs.secret FROM " + this.bucket._name + " AS account USE KEYS $account UNNEST account.addresses AS keypairs WHERE keypairs.address = $address”; var query = Couchbase.N1qlQuery.fromString(statement); return new Promise((resolve, reject) => { this.bucket.query(query, { “account”: account, “address”: address }, (error, result) => { if(error) { reject({ “code”: error.code, “message”: error.message }); } resolve({ “secret”: result[0] }); }); });}给定一个特定的帐户,我们创建一个类似于我们之前看到的查询。这次,在我们UNNEST,我们执行WHERE条件,仅为匹配地址提供结果。如果我们想要我们可以做一个数组操作。使用Couchbase和N1QL,有很多方法可以解决问题。======================================================================分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程:java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。EOS教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。汇智网原创翻译,转载请标明出处。这里是原文Node.js和NoSQL开发比特币加密货币应用程序 ...

December 24, 2018 · 3 min · jiezi

Node.js和NoSQL开发比特币加密货币应用程序(下)

在使用Node.js和NoSQL开发比特币加密货币应用程序(上)中,我们创建了HD钱包,它可以为给定的种子生成无限量的密钥,每个密钥代表一个用户钱包。我们将根据主种子创建每个包含钱包的用户帐户。下面我们接着来看如何进行交易、查询余额等重要功能如何实现。我们要在这里改变一下。到目前为止,我们已经在NoSQL数据库中完成了面向帐户的操作。另一个重要方面是交易。例如,也许用户X为BTC存入一些美元货币,而用户Y进行提款。我们需要存储和查询该交易信息。API端点函数将保存交易数据,但我们仍然可以查询它。getAccountBalance(account) { var statement = “SELECT SUM(tx.satoshis) AS balance FROM " + this.bucket._name + " AS tx WHERE tx.type = ’transaction’ AND tx.account = $account”; var query = Couchbase.N1qlQuery.fromString(statement); return new Promise((resolve, reject) => { this.bucket.query(query, { “account”: account }, (error, result) => { if(error) { reject({ “code”: error.code, “message”: error.message }); } resolve({ “balance”: result[0].balance }); }); });}给定一个帐户,我们希望获得特定用户的帐户余额。等一下,让我们退后一步,因为我们不是已经创建了一些帐户余额功能吗?从技术上讲,我们做了,但这些功能用于检查钱包余额,而不是帐户余额。这是我的一些经验变成灰色区域的地方。每次发送比特币时,都会收取费用,有时费用相当昂贵。当你存款时,将钱转入你的钱包并不符合成本效益,因为这将收取矿工费。然后你将被收取撤回甚至转账的费用。那时你已经失去了大部分的比特币。相反,我认为交易所有一个类似于证券交易所货币市场账户的持有账户。你的帐户中应该有资金的记录,但从技术上讲,它不在钱包中。如果你想要转账,则需要从应用程序地址而不是你的用户地址进行转账。当你退出时,它只是被减去。再说一次,我不知道这是否真的如何运作,但这就是我为了避免各处收费而采取的方式。回到我们的getAccountBalance函数。我们正在处理每笔交易的总和。存款具有正值,而转账和取款具有负值。将这些信息汇总在一起可以为你提供准确的数字,不包括你的钱包余额。稍后我们将获得一个钱包余额帐户。鉴于我们对帐户余额知之甚少,我们可以尝试从钱包中创建一个交易:createTransactionFromAccount(account, source, destination, amount) { return new Promise((resolve, reject) => { this.getAddressBalance(source).then(sourceAddress => { if(sourceAddress.balanceSat < amount) { return reject({ “message”: “Not enough funds in account.” }); } this.getPrivateKeyFromAddress(account, source).then(keypair => { this.getAddressUtxo(source).then(utxo => { var transaction = new Bitcore.Transaction(); for(var i = 0; i < utxo.length; i++) { transaction.from(utxo[i]); } transaction.to(destination, amount); this.addAddress(account).then(change => { transaction.change(change.address); transaction.sign(keypair.secret); resolve(transaction); }, error => reject(error)); }, error => reject(error)); }, error => reject(error)); }, error => reject(error)); });}如果提供了源地址,目的地地址和金额,我们可以创建并签署一个交易,以便稍后在比特币网络上广播。首先,我们得到有问题的源地址的余额。我们需要确保它有足够的UTXO来满足发送量预期。请注意,在此示例中,我们正在执行单个地址交易。如果你想变得复杂,可以在单个交易中从多个地址发送。我们不会在这里这样做。如果我们的单个地址有足够的资金,我们会获得它的私钥和UTXO数据。使用UTXO数据,我们可以创建比特币交易,应用目的地地址和更改地址,然后使用我们的私钥对交易进行签名。可以广播响应。同样地,假设我们想从我们的持有账户转账比特币:createTransactionFromMaster(account, destination, amount) { return new Promise((resolve, reject) => { this.getAccountBalance(account).then(accountBalance => { if(accountBalance.balance < amount) { reject({ “message”: “Not enough funds in account.” }); } var mKeyPairs = this.getMasterKeyPairs(); var masterAddresses = mKeyPairs.map(a => a.address); this.getMasterAddressWithMinimum(masterAddresses, amount).then(funds => { this.getAddressUtxo(funds.address).then(utxo => { var transaction = new Bitcore.Transaction(); for(var i = 0; i < utxo.length; i++) { transaction.from(utxo[i]); } transaction.to(destination, amount); var change = helper.getMasterChangeAddress(); transaction.change(change.address); for(var j = 0; j < mKeyPairs.length; j ++) { if(mKeyPairs[j].address == funds.address) { transaction.sign(mKeyPairs[j].secret); } } var tx = { account: account, satoshis: (amount * -1), timestamp: (new Date()).getTime(), status: “transfer”, type: “transaction” }; this.insert(tx).then(result => { resolve(transaction); }, error => reject(error)); }, error => reject(error)); }, error => reject(error)); }, error => reject(error)); });}我们假设我们的交换地址装满了疯狂的比特币以满足需求。第一步是确保我们的持有账户中有资金。我们可以执行总结每个交易的查询以获得有效数字。如果我们有足够的,我们可以获得所有10个主密钥对和地址。我们需要检查哪个地址有足够的资金发送。请记住,这里的单一地址交易可能会有更多。如果地址有足够的资金,我们会获得UTXO数据并开始进行交易。这次代替我们的钱包作为源地址,我们使用交换的钱包。在我们获得签名交易之后,我们想在数据库中创建一个交易来减去我们正在传输的值。在我们进入API端点之前,我想重新尝试一些事情:我假设热门的交易所有一个持有账户,以避免对钱包地址征收费用。我们在此示例中使用单地址交易,而不是聚合我们拥有的内容。我应该是在加密帐户文档中的关键数据。我没有广播任何交易,只创建它们。现在让我们关注我们的API端点,这是一个简单的部分。使用Express Framework设计RESTful API端点请记住,正如我们在开始时配置的那样,我们的端点将分为三个文件,这些文件充当分组。我们将从最小和最简单的端点组开始,这些端点比其他任何端点都更实用。打开项目的routes/utility.js文件并包含以下内容:const Bitcore = require(“bitcore-lib”);const Mnemonic = require(“bitcore-mnemonic”);module.exports = (app) => { app.get("/mnemonic", (request, response) => { response.send({ “mnemonic”: (new Mnemonic(Mnemonic.Words.ENGLISH)).toString() }); }); app.get("/balance/value", (request, response) => { Request(“https://api.coinmarketcap.com/v1/ticker/bitcoin/").then(market => { response.send({ “value”: “$” + (JSON.parse(market)[0].price_usd * request.query.balance).toFixed(2) }); }, error => { response.status(500).send(error); }); });}这里我们有两个端点,一个用于生成助记符种子,另一个用于获取比特币余额的法定值。这两者都不是真正必要的,但是在第一次启动时,生成种子值以便稍后保存在我们的配置文件中可能会很好。现在打开项目的routes/account.js文件,以便我们处理帐户信息:const Request = require(“request-promise”);const Joi = require(“joi”);const helper = require(”../app").helper;module.exports = (app) => { app.post("/account", (request, response) => { }); app.put("/account/address/:id", (request, response) => { }); app.get("/account/addresses/:id", (request, response) => { }); app.get("/addresses", (request, response) => { }); app.get("/account/balance/:id", (request, response) => { }); app.get("/address/balance/:id", (request, response) => { });}请注意,我们正在从尚未启动的app.js文件中提取helper程序类。现在就跟它一起使用它以后会有意义,虽然它没什么特别的。在创建帐户时,我们有以下内容:app.post("/account", (request, response) => { var model = Joi.object().keys({ firstname: Joi.string().required(), lastname: Joi.string().required(), type: Joi.string().forbidden().default(“account”) }); Joi.validate(request.body, model, { stripUnknown: true }, (error, value) => { if(error) { return response.status(500).send(error); } helper.createAccount(value).then(result => { response.send(value); }, error => { response.status(500).send(error); }); });});使用Joi我们可以验证请求正文并在错误时抛出错误。假设请求正文是正确的,我们可以调用createAccount函数在数据库中保存一个新帐户。创建帐户后,我们可以添加一些地址:app.put("/account/address/:id", (request, response) => { helper.addAddress(request.params.id).then(result => { response.send(result); }, error => { return response.status(500).send(error); });});使用发送的帐户ID,我们可以调用我们的addAddress函数来对我们的文档使用子文档操作。还不错吧?要获取特定帐户的所有地址,我们可能会有以下内容:app.get("/account/addresses/:id", (request, response) => { helper.getAddresses(request.params.id).then(result => { response.send(result); }, error => { response.status(500).send(error); });});或者,如果我们不提供id,我们可以使用以下端点函数从所有帐户获取所有地址:app.get("/addresses", (request, response) => { helper.getAddresses().then(result => { response.send(result); }, error => { response.status(500).send(error); });});现在可能是最棘手的端点功能。假设我们希望获得帐户余额,其中包括持有帐户以及每个钱包地址。我们可以做到以下几点:app.get("/account/balance/:id", (request, response) => { helper.getAddresses(request.params.id).then(addresses => helper.getWalletBalance(addresses)).then(balance => { helper.getAccountBalance(request.params.id).then(result => { response.send({ “balance”: balance.balance + result.balance }); }, error => { response.status(500).send({ “code”: error.code, “message”: error.message }); }); }, error => { response.status(500).send({ “code”: error.code, “message”: error.message }); });});以上将调用我们的两个函数来获得余额,并将结果加在一起以获得一个巨大的余额。帐户端点不是特别有趣。创建交易更令人兴奋。打开项目的routes/transaction.js文件并包含以下内容:const Request = require(“request-promise”);const Joi = require(“joi”);const Bitcore = require(“bitcore-lib”);const helper = require("../app").helper;module.exports = (app) => { app.post("/withdraw", (request, response) => { }); app.post("/deposit", (request, response) => { }); app.post("/transfer", (request, response) => { });}我们有三种不同类型的交易。我们可以为比特币存入法定货币,为法定货币提取比特币,并将比特币转账到新的钱包地址。我们来看看存款端点:app.post("/deposit", (request, response) => { var model = Joi.object().keys({ usd: Joi.number().required(), id: Joi.string().required() }); Joi.validate(request.body, model, { stripUnknown: true }, (error, value) => { if(error) { return response.status(500).send(error); } Request(“https://api.coinmarketcap.com/v1/ticker/bitcoin/").then(market => { var btc = value.usd / JSON.parse(market)[0].price_usd; var transaction = { account: value.id, usd: value.usd, satoshis: Bitcore.Unit.fromBTC(btc).toSatoshis(), timestamp: (new Date()).getTime(), status: “deposit”, type: “transaction” }; helper.insert(transaction).then(result => { response.send(result); }, error => { response.status(500).send(error); }); }, error => { response.status(500).send(error); }); });});在我们验证输入后,我们使用CoinMarketCap检查美元比特币的当前值。使用响应中的数据,我们可以根据存入的美元金额计算出应该获得多少比特币。创建数据库交易后,我们可以保存它,因为它是一个正数,它将在查询时返回正余额。现在让我们说我们想从比特币中提取资金:app.post("/withdraw”, (request, response) => { var model = Joi.object().keys({ satoshis: Joi.number().required(), id: Joi.string().required() }); Joi.validate(request.body, model, { stripUnknown: true }, (error, value) => { if(error) { return response.status(500).send(error); } helper.getAccountBalance(value.id).then(result => { if(result.balance == null || (result.balance - value.satoshis) < 0) { return response.status(500).send({ “message”: “There are not " + value.satoshis + " satoshis available for withdrawal” }); } Request(“https://api.coinmarketcap.com/v1/ticker/bitcoin/").then(market => { var usd = (Bitcore.Unit.fromSatoshis(value.satoshis).toBTC() * JSON.parse(market)[0].price_usd).toFixed(2); var transaction = { account: value.id, satoshis: (value.satoshis * -1), usd: parseFloat(usd), timestamp: (new Date()).getTime(), status: “withdrawal”, type: “transaction” }; helper.insert(transaction).then(result => { response.send(result); }, error => { response.status(500).send(error); }); }, error => { response.status(500).send(error); }); }, error => { return response.status(500).send(error); }); });});类似的事件正在这里发生。在验证请求主体后,我们获得帐户余额并确保我们提取的金额小于或等于我们的余额。如果是,我们可以根据CoinMarketCap的当前价格进行另一次交易。我们将使用负值创建一个交易并将其保存到数据库中。在这两种情况下,我们都依赖于CoinMarketCap,它在过去一直存在负面争议。你可能希望为交易选择不同的资源。最后,我们有转账:app.post("/transfer”, (request, response) => { var model = Joi.object().keys({ amount: Joi.number().required(), sourceaddress: Joi.string().optional(), destinationaddress: Joi.string().required(), id: Joi.string().required() }); Joi.validate(request.body, model, { stripUnknown: true }, (error, value) => { if(error) { return response.status(500).send(error); } if(value.sourceaddress) { helper.createTransactionFromAccount(value.id, value.sourceaddress, value.destinationaddress, value.amount).then(result => { response.send(result); }, error => { response.status(500).send(error); }); } else { helper.createTransactionFromMaster(value.id, value.destinationaddress, value.amount).then(result => { response.send(result); }, error => { response.status(500).send(error); }); } });});如果请求包含源地址,我们将从我们自己的钱包转账,否则我们将从交换管理的钱包转账。所有这些都基于我们之前创建的功能。通过端点,我们可以专注于引导我们的应用程序并得出结论。引导Express Framework应用程序现在我们有两个文件保持不受示例的影响。我们还没有添加配置或驱动逻辑来引导我们的端点。打开项目的config.json文件,并包含以下内容:{ “mnemonic”: “manage inspire agent october potato thought hospital trim shoulder round tired kangaroo”, “host”: “localhost”, “bucket”: “bitbase”, “username”: “bitbase”, “password”: “123456”}记住这个文件非常敏感。考虑将其锁定或甚至使用不同的方法。如果种子被暴露,则可以毫不费力地获得所有用户帐户和交换帐户的每个私钥。现在打开项目的app.js文件并包含以下内容:const Express = require(“express”);const BodyParser = require(“body-parser”);const Bitcore = require(“bitcore-lib”);const Mnemonic = require(“bitcore-mnemonic”);const Config = require("./config");const Helper = require("./classes/helper");var app = Express();app.use(BodyParser.json());app.use(BodyParser.urlencoded({ extended: true }));var mnemonic = new Mnemonic(Config.mnemonic);var master = new Bitcore.HDPrivateKey(mnemonic.toHDPrivateKey());module.exports.helper = new Helper(Config.host, Config.bucket, Config.username, Config.password, master);require("./routes/account.js")(app);require("./routes/transaction.js")(app);require("./routes/utility.js")(app);var server = app.listen(3000, () => { console.log(“Listening at :” + server.address().port + “…”);});我们正在做的是初始化Express,加载配置信息以及链接我们的路由。module.exports.helper变量是我们的单例,将在每个其他JavaScript文件中使用。结论你刚刚了解了如何使用Node.js和Couchbase作为NoSQL数据库来构建自己的加密货币交易。我们涵盖了很多,从生成HD钱包到创建具有复杂数据库逻辑的端点。我不能强调这一点。我是加密货币爱好者,在金融领域没有真正的经验。我分享的东西应该有效,但可以做得更好。不要忘记加密密钥并确保种子安全。测试你的工作,知道自己正在做什么。如果你想下载此项目,请在GitHub上查看。如果你想分享关于该主题的见解,经验等,请在评论中分享。社区可以努力创造伟大的东西!如果你是Golang的粉丝,我在之前的教程中创建了一个类似的项目。======================================================================分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程:java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。EOS教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。汇智网原创翻译,转载请标明出处。这里是原文使用Node.js和NoSQL开发比特币加密货币应用程序 ...

December 24, 2018 · 4 min · jiezi