2019年首发,有耐心且多思考

本博客 猫叔的博客,转载请申明出前言最近半个月基本没有碰到代码层面上的东西,所以突然写技术的都点干巴巴,开年首秀还是来点传统的鸡汤吧。你以为的努力,在别人那里就是不值一提我天天加班,所以挺厉害的;我项目经验多,自我感觉跳槽一定能涨薪;我时长了解新的技术,应该不会太落后……现在很多程序员或者说做技术的年轻人都比较浮躁,他们容易心切,对于自己的万分之一努力看得很重,而却没有持之以恒的坚持,而且没有反省自身的不足与劣势。我以前也会自负,自以为很了不起,比如年级轻轻当上面试官,对自己的技术感觉很是牛bi,但是没有几个月我就被人打下谷底,这是好事,对现在的我来说,每一次困惑都是一次成长,我想做出属于自己的选择与努力。我总是觉得自己时间不够,我想更多的学习,不论是技术还是产品,我都需要去努力,我的职业发展规划是软件架构师,不过公司高层却统一觉得我更适合做产品经理,所以我现在更多是两边兼顾。不过我并不排斥任何一个方向,因为只要与IT相关,我都很是喜欢。2018年末,有人说是互联网寒潮,很多程序员被辞退,未来的十年很多人说会更加惨淡,但是从个人角度出发,我还是一名程序员,因为一开始我就是从事这份工作的,未来我依旧喜欢程序员这个名词。我希望大家可以很好的努力,戒骄戒躁。如果你完成的每一个需求,都十分了解业务那你会是一个优秀的技术实现工作者,代码可能没什么,但是能够精准解决问题的代码就值钱了,现在想起来,我也曾经写过无数不值一提的代码,当时那些都是我的积累,是我对于技术实现的经验积累,以至于到后期我在面对各种业务需求的时候,我可以立马想到实现的几种方式与后期业务扩展后,采用哪一种可以更有助于扩展或者重构。你需要去与团队交流,你需要有耐心,你需要精准的了解产品经理下达的每一个需求与业务场景,你也可以提出合理的建议,比如删除部分功能为了产品更好的针对目标用户。对于技术实现,希望你能多学几招如果你对于一个CMS(内容管理系统)的业务是基本了解,那么你会有哪几种思路去实现呢?如果是一个电商网站又如何?一个自己运营的电商网站,或者是一个2B的平台电商网站,又有什么区别?你在设计数据库的时候要如何考虑?如果以上都是成熟的,你要针对他们设计一个统一关联的广告投放系统,你又要如何处理?每当你看到一些APP时,试想一下你的技术栈是否可以实现这一整个APP,实现后会有什么问题?多思考和看书总是好的这应该是一个建议,因为我至今都还没有做好,这个简单的建议。公众号:Java猫说现架构设计(码农)兼创业技术顾问,不羁平庸,热爱开源,杂谈程序人生与不定期干货。

February 11, 2019 · 1 min · jiezi

记录一次过程(1):Building Mosquitto from the git repository

MosquittoMosquittto是针对MQTT 3.1版本和3.1.1版本的一个开源实现的服务器。它包含C和C++的客户端库,以及用于publish/subscribe的实用程序:mosquitto_pub/mosquitto_sub。Mosquitto项目对于如何Building from source描述并不足够清晰,但其实步骤也还算简单。步骤1.cmake使用命令cmake -DWITH_TLS_PSK=OFF -DWITH_TLS=OFF得到– Configuring done– Generating done这两行中间有warning,由于我只是需要搭建一个简单的环境做简单地测试,就没有管warning。2.make使用命令make得到[100%] Linking C executable mosquitto[100%] Built target mosquitto3.sudo make install使用命令sudo make install报错。报错信息如下:Install the project…– Install configuration: “”– Up-to-date: /usr/local/etc/mosquitto/mosquitto.conf– Up-to-date: /usr/local/etc/mosquitto/aclfile.example– Up-to-date: /usr/local/etc/mosquitto/pskfile.example– Up-to-date: /usr/local/etc/mosquitto/pwfile.example– Up-to-date: /usr/local/lib/pkgconfig/libmosquitto.pc– Up-to-date: /usr/local/lib/pkgconfig/libmosquittopp.pc– Installing: /usr/local/lib/libmosquitto.1.5.5.dylib– Up-to-date: /usr/local/lib/libmosquitto.1.dylib– Up-to-date: /usr/local/lib/libmosquitto.dylib– Up-to-date: /usr/local/include/mosquitto.h– Installing: /usr/local/lib/libmosquittopp.1.5.5.dylib– Up-to-date: /usr/local/lib/libmosquittopp.1.dylib– Up-to-date: /usr/local/lib/libmosquittopp.dylib– Up-to-date: /usr/local/include/mosquittopp.h– Installing: /usr/local/bin/mosquitto_pub– Installing: /usr/local/bin/mosquitto_sub– Installing: /usr/local/sbin/mosquitto– Up-to-date: /usr/local/include/mosquitto_broker.h– Up-to-date: /usr/local/include/mosquitto_plugin.hCMake Error at man/cmake_install.cmake:36 (file): file INSTALL cannot find “/Users/xxxx/Desktop/iMac-mosquitto/mosquitto-master/man/mosquitto_passwd.1”.Call Stack (most recent call first): cmake_install.cmake:57 (include)make: * [install] Error 1报错之后首先检查报错信息,google之后仍然有解决方面的困难。这时我们注意到上面加粗的三行,切换到安装目录 /usr/local/include/ 后运行命令 mosquitto发现可以运行,但是在安装目录外就不可以运行。这时候我们的下一步就是配置环境变量,从而使mosquitto的运行不受当前目录的限制。4.添加环境变量环境变量的作用是:可以在操作系统的各个目录下,都能访问到需要的工具目录内的内容。我参考的是Mac OS X 系统的环境变量配置分别将/usr/local/bin/以及/usr/local/sbin/加入环境变量之后(我将路径加入了用户级的~/.bash_profile),就可以运行mosquitto了。但是还有一个烦人的小问题,就是Mac每次都要执行source /.bash_profile,配置的环境变量才生效。解决方法是:在/.zshrc文件最后,增加一行: source ~/.bash_profile5.运行测试先开一个terminal,输入命令mosquitto。打开第二个terminal,输入命令mosquitto_sub -h localhost -t test。打开第三个terminal,输入命令mosquitto_pub -h localhost -t test -m “hello world”。发现在第二个terminal上出现了hello world字符串,即成功。6.思考几个问题,后续解决。直接用cmake .是会由于OpenSSL的路径问题报错的,OpenSSL是干什么用的?我没有去解决OpenSSL的路径问题,而是忽略了它,关闭了TLS直接cmake的。TLS又是什么?起到了什么作用?程序运行起来经过了哪些步骤?一个大型项目是如何安装运行起来的?为什么安装mosquitto的步骤是cmake->make->sudo make install? ...

February 11, 2019 · 1 min · jiezi

什么是Github的元数据metadata以及如何备份github上的数据

github被微软收购后,提供的工具种类是越来越多了,大大提高了我们程序员日常工作的效率。今天我偶然发现,我们可以一键把自己整个github上的数据,不仅仅是代码,还包含每个仓库里创建的issue都轻松下载到本地进行备份。点这个setting按钮:点Account超链接:新建一个export作业 - 导出作业。过了一会,我们github注册的邮箱地址会收到一封邮件,点击Download export就可以下载一个压缩包到本地:我这个账号的所有github数据不export不知道,一export才吓一跳,竟然有1.7个G那么大。下载下来的文件是一个巨大的压缩包,解开之后是这样子的:随便看几个文件的内容。比如这个commit_comment的json文件:实际就是指的这个commit:这是我github上一个仓库的issue的明细:通过上图的url打开这个issue,发现内容和我通过工具下载的内容一致:我所有上传到github issue里的图片也会通过附件的方式被下载到本地,存储在这些文件夹里:希望这个工具对大家备份自己在github网站上的数据有所帮助。要获取更多Jerry的原创文章,请关注公众号"汪子熙":

February 3, 2019 · 1 min · jiezi

Zilliqa主网上线,哪些值得期待

2018年12月26日Xinshu Dong发布于Zilliqa博客自从我们第一次向 GitHub 代码库(最早的项目名还是nuQoin)提交代码以来,已经过去一年半的时间了。在第一次提交代码之后,我们着手构建了一个安全、可扩展的区块链平台。现在,在选择了一个更好的名称、进行了7,000多次提交、经历了无数个小时努力之后,我们热切地等待 2019年1月31日Zilliqa主网发布。重温起点,对我们来说,这是一次非常独特的科学和工程学之旅。每一步,我们都吸取了教训,遇到了不可预见的挑战,并在需要时改进协议设计。一路走来,无论是技术领域还是非技术领域,我们都得到了社区极大帮助。我们衷心感谢他们对这个项目的不懈支持。这项成就既是我们的,也是你们的。恭喜大家!在这篇博客文章中,我们想更详细地分享Zilliqa主网在启动时所具备的功能,以及这个新生网络的保护机制。主网功能在Zilliqa,我们的目标一直是建立一个更好的区块链平台——一个能够支持去中心化应用和产品的生态系统。特别是,我们专注于创建一个平台,为最终用户带来真正的价值,并推动区块链技术实现有意义落地采用。要使应用程序可行,用于构建它的平台不仅要稳固、可扩展,而且要安全,这一点至关重要。这就是为什么安全对我们来说是一个核心的优先事项,并在很大程度上推动我们的决策。即将发布的主网捆绑了以分片为核心的极具创新性的功能。下面我们将讨论Zilliqa主网的一些核心功能:分片:Zilliqa网络支持常规支付交易和需调用智能合约交易的分片。在分片架构上处理智能合约交易有其自身的一系列挑战。想了解更多关于Zilliqa解决该问题的方法,请查看这篇博客文章(https://blog.zilliqa.com/prov…)。实用拜占庭容错式(Practical Byzantine Fault Tolerance,简称为 PBFT)区块链:Zilliqa将是目前市面上为数不多的PBFT区块链。PBFT的共识机制既高效,又能赋予交易最终性,因此不需要确认。安全智能合约:Zilliqa协议附带了一种名为「Scilla」的全新智能合约语言。该语言旨在消除现有智能合约中许多已知漏洞,并使其易于进行形式化验证。双启动环保挖矿:对于以太坊和Zilliqa这样基于ethash的PoW区块链,进行双挖是可行的。这是因为Zilliqa结合了PoW和PBFT,其中PoW仅用于防范Sybil攻击,而PBFT则用于达成共识。由于在 Zilliqa上的PoW周期每2-3小时只需运行1分钟,我们认为,这与每个区块达成共识都需要使用 PoW的区块链相比,Zilliqa上挖矿的能源足迹要小得多。块奖励的方差很小:Zilliqa 协议采用一种创新的激励机制,通过衡量矿工在共识协议中的贡献来予以奖励。因此,成千上万(或更多)矿工可以因一个区块获得奖励,从而实现方差很低。保护初始网络免受潜在攻击与比特币和以太坊等其他区块链一样,Zilliqa区块链网络能够处理交易并维护分布式账本的全局状态。不过不幸的是,不同的区块链网络在潜在攻击者方面存在一些显著的差异。一边是比特币,它一开始在几乎没有什么估值的情况下已经在有机增长;另一边是Zilliqa,它在今天已经具有相当大的价值。这种估值上的差异导致了在Zilliqa网络还处于萌芽阶段时就会面临威胁,而在比特币这样的网络刚起步时,这类威胁并不一定是严重的问题。考虑到这一点,Zilliqa主网在发布时将带有保护机制和保守模式,从而尽量减少对这个新生网络发起攻击的机会。让我们进一步详细描述这些机制:启动阶段:我们首先将在启动阶段启动网络。我们预计,为了使区块链运行得更快、更高效、更安全,需要一些时间来收集所需的算力。我们需要确保我们的网络在这个哈希算力相对较低的初始启动期间不受攻击。在启动阶段,矿工将获得采矿奖励,但不会处理任何交易。一旦特定的区块数被挖掘后,这个启动阶段将自动结束。我们预计这个时间点将是2019年3月前后。保护节点:在新Zilliqa网络的初期阶段,我们有一种机制可以自动生成新的节点加入网络,以达到保护目的(保护节点)。这些节点的数量是动态调整的,当网络低于预期大小或检测到严重不稳定时,节点数量将增加。这些节点将由Zilliqa团队运行,可以让我们更容易快速修复在主网上线后可能出现的任何错误或问题。为了确保这些节点即使在对抗的情况下也能够加入网络,它们在PoW阶段获得了更高的优先级,为了公平起见,它们不会获得任何挖矿奖励。随着时间的推移,网络趋于稳定、挖矿难度增加,保护节点的数量会逐渐降低,最终被社区运营的其他节点所取代。查找节点和种子节点:像Zilliqa这样具有高吞吐能力的区块链,预计很快便会生成大量数据,不能指望所有节点都存储全部交易历史。虽然不是每个客户端或去中心化应用程序都需要这样的信息,但是某些应用程序(如区块资源管理器、交易所和钱包)需要这类信息来运行。在Zilliqa中,我们从运行共识的节点上卸下全部存储责任,同时设置查找节点和种子节点来维护交易历史和区块链的全局状态。为了提高通信效率和抵御DoS攻击的能力,即将上线的Zilliqa主网利用种子节点作为用户和客户端访问Zilliq 网络的网关。种子节点进一步合并所有用户的交易请求,然后将它们转发到查找节点。查找节点在开始时将由Zilliqa团队运行。而种子节点是终端用户和客户端的直接访问点。种子节点可能由Zilliqa团队、交易所、钱包和区块资源管理器托管,随着时间的推移,也将交给社区托管。目前,我们已有计划,设计一个安全有效的协议来进一步让查找节点和种子节点去中心化,使网络更加开放。随着Zilliqa主网推出,我们朝着那些让该平台应用起来的各种令人兴奋的机会迈出了一大步。展望未来,我们将继续秉承区块链创新的高标准,与社区和不同行业紧密合作,将现实世界的应用案例引入Zilliqa平台。

February 1, 2019 · 1 min · jiezi

Zilminer测试版发布

亲爱的矿工们,我很高兴和大家宣布Zilminer(ethminer 的分叉)正式开发了。Zilminer允许你在Ubuntu和Windows两个系统下都可以远程挖掘Zilliqa。这款Zilminer支持所有ethminer的当前功能和一些新的Zilliqa的特定功能。Zilminer为您挖掘Zilliqa时提供了一定的便利。当处在Zilliqa PoW窗口时,Zilminer会自动启动您的GPU设备来进行GPU挖掘。当CPU节点正在运行pBFT共识算法时,Zilminer暂停您GPU设备的运程。Zilminer v0.1.16:Beat此版本的Zilminer允许矿工在代理服务器的帮助下通过Getwork协议对1个CPU节点或CPU cluster进行代理挖掘。· 如想将许多数1GPU设备链接上1个CPU节点。请参阅选项1。· 如想将许多数GPU设备链接上多数CPU节点(CPU cluster)。请参阅选项2。选项1:将许多GPU设备链接上1个CPU节点设置架构如以下图所示,两方之间的所有通信都是通过JSON-RPC进行。· Zilliqa节点将在1个CPU节点上运行pBFT的程序来获取奖励。· GPU设备将在独立的GPU集群上运行Zilminer来进行PoW挖掘,并直接向CPU节点提供PoW解决方案。要将多数GPU设备连接到1个CPU节点,您需要执行以下步骤:步骤1.在所有GPU设备上下载Zilminer客户端· 对于Windows:下载地址https://github.com/DurianStal…· 对于Ubuntu:下载地址https://github.com/DurianStal…步骤2.在CPU节点上下载并设置Zilliqa客户端注意:Zilliqa客户端仅支持Ubuntu 16.04 OS版本。您可以按照wiki挖矿指南(https://github.com/Zilliqa/Zi…。但是,在启动之前,您必须在配置文件中编辑constants.xml文件的以下参数:·设置 GETWORK_SERVER_MINE 值为 true.·设置 GETWORK_SERVER_PORT 为您将使用的GetWork的端口。(默认是 4202)·将其他挖掘参数设置为 false.<CUDA_GPU_MINE>false</CUDA_GPU_MINE><FULL_DATASET_MINE>false</FULL_DATASET_MINE><OPENCL_GPU_MINE>false</OPENCL_GPU_MINE><REMOTE_MINE>false</REMOTE_MINE>执行此操作后,您需要使用以下命令查找你当前的IP地址并记下来:curl https://ipinfo.io/ip步骤3.设置Zilminer客户端在命令行窗口键入以下命令:zilminer –max-submit=1 –farm-recheck 10000 –work-timeout=7200 –farm-retries=10 –retry-delay=10 -P zil://wallet_address.worker_name@zil_node_ip:get_work_port注意:请更改您的wallet_address,worker_name,zil_node_ip和 get_work_port 。· 对于wallet_address:您可以使用Moonlet钱包(https://moonlet.xyz/)来创建新的KeyPair和Zilliqa的地址。· 对于 worker_name:您可以您输入想要的任意工作者姓名。· 对于 zil_node_ip:请输入您记下的Zilliqa节点的IP地址。· 对于 get_work_port:请输入在GETWORK_SERVER_PORT中使用的端口。默认是 4202。选项2:将许多GPU设备设置为多个CPU节点设置架构如以下图所示,三方之间的所有通信都是通过JSON-RPC进行的。· Zilliqa节点将在一个CPU集群上运行来处理事务并执行pBFT共识算法以获得奖励。· GPU设备将在独立的GPU集群上运行Zilminer来进行PoW挖掘,并通过Mining代理向CPU节点提供PoW解决方案。· Mining代理将处理来自CPU集群的挖矿请求,并处理来自GPU集群的挖矿注册/响应(Register/Response)。要将多个GPU设备连接到多个CPU节点,您需要执行以下步骤:步骤1.在你的所有GPU设备上下载Zilminer客户端· 对于Windows:下载地址https://github.com/DurianStal…· 对于Ubuntu:下载地址https://github.com/DurianStal…步骤2.设置你的Zilliqa Mining代理服务器注意:Zilliqa Mining代理服务器仅支持Ubuntu 16.04 OS版本。您将需要一个单独的CPU来创建此Mining代理服务器。建议的设置是在GCP或AWS实例中托管此代理服务器。(例如t2.medium EC2实例)请按照 README.md(https://github.com/deepgully/…) 中的指南来设置挖掘代理服务器。Mining代理Github:https://github.com/DurianStal…您需要获取此处为下一步设置的api_server的URL。步骤3.在你所有的CPU节点上下载并设置Zilliqa客户端注意:Zilliqa客户端仅支持Ubuntu 16.04 OS版本。您可以按照wiki挖矿指南(https://github.com/Zilliqa/Zi…)在docker或本地build环境中在你的所有CPU上设置Zilliqa客户端。建议的设置是使用docker镜像创建一个CPU集群并使用Kubernetes处理这个CPU集群。一个CPU节点可以是GCP或AWS实例。用于1个CPU节点的推荐云实例就是t2.large EC2实例。对于所有CPU节点,在启动之前,您必须在配置文件中编辑constants.xml文件的以下参数:· 设置 REMOTE_MINE的值true· 设置MINING_PROXY_URL 为 Mining 代理服务器的URL。(例如http://127.0.0.1:4202/api)· 将其他 Mining 参数设置为 true<CUDA_GPU_MINE>false</CUDA_GPU_MINE><FULL_DATASET_MINE>false</FULL_DATASET_MINE><OPENCL_GPU_MINE>false</OPENCL_GPU_MINE><GETWORK_SERVER_MINE>false</GETWORK_SERVER_MINE>步骤4.设置Zilminer客户端在命令行窗口键入以下命令:zilminer –max-submit=1 –farm-recheck 10000 –work-timeout=7200 –farm-retries=10 –retry-delay=10 -P zil://wallet_address.worker_name@zil_node_ip:get_work_port注意:请更改您的wallet_address,worker_name,zil_node_ip 和 get_work_port 。· 对于 wallet_address:您可以使用Moonlet钱包(https://moonlet.xyz/)来创建新的 KeyPair 和 Zilliqa 的地址。· 对于 worker_name:您可以您输入想要的任意工作者姓名。· 对于 proxy_ip:请输入代理 api_server 的IP地址。· 对于 proxy_port:请输入代理 api_server的端口。默认是 4202。其他有用的链接· 挖矿奖励计算器:https://tinyurl.com/reward-ca…· Python 3.6安装:https://www.python.org/downlo…· MongoDB安装:https://docs.mongodb.com/manu…Github上的源代码DurianStallSingapore/ZILMiner通过在GitHub上创建帐户,为DurianStallSingapore / ZILMiner开发做出贡献。DurianStallSingapore/Zilliqa-Mining-ProxyZilliqa节点和矿工之间的Mining代理。通过在GitHub上创建一个帐户,为DurianStallSingapore/Zilliqa-Mining-Proxy开发做出贡献。 ...

February 1, 2019 · 1 min · jiezi

Github 生成新的SSH密钥并添加至SSH-Agent

问题:hexo d 时出现错误 Error: ERROR: Permission toremote: Permission to huweihuang/hexo-theme-huweihuang.git denied to wanghaijuan.fatal: unable to access ‘https://github.com/huweihuang/hexo-theme-huweihuang/': The requested URL returned error: 403FATAL Something’s wrong. Maybe you can find the solution here: http://hexo.io/docs/troubleshooting.htmlError: remote: Permission to huweihuang/hexo-theme-huweihuang.git denied to wanghaijuan.fatal: unable to access ‘https://github.com/huweihuang/hexo-theme-huweihuang/': The requested URL returned error: 403 at ChildProcess.<anonymous> (D:\project\blog\hexo-huweihuang\node_modules\hexo-deployer-git\node_modules\hexo-util\lib\spawn.js:37:17) at emitTwo (events.js:126:13) at ChildProcess.emit (events.js:214:7) at ChildProcess.cp.emit (D:\project\blog\hexo-huweihuang\node_modules\cross-spawn-async\lib\enoent.js:37:29) at maybeClose (internal/child_process.js:925:16) at Process.ChildProcess._handle.onexit (internal/child_process.js:209:5)原因:huweihuang/hexo-theme-huweihuang.git 拒绝接受 wanghaijuan。 说明当前有两个github账号,可以更新博客, 新的github账号没有配置好。解决办法:在Github生成新的SSH密钥配置步骤:一、生成新的SSH密钥1、打开Git Bash。2、 进入.ssh 根目录下。cd /.ssh3、输入GitHub电子邮件地址,其中your_email@example.com是您关联的GitHub邮箱。ssh-keygen -t rsa -b 4096 -C “your_email@example.com"4、输入保存的密钥文件并接受默认文件地址,按Enter键// 用提供的电子邮件作为标签创建一个新的ssh密钥。(无需操作)Generating public/private rsa key pair. // 输入名称并按Enter键,其中hexo_rsa可自行设置Enter a file in which to save the key (/c/Users/you/.ssh/id_rsa):hexo_rsa5、输入安全密码,没有则为空,按Enter键进行下一步Enter passphrase (empty for no passphrase): [Type a passphrase]Enter same passphrase again: [Type passphrase again]此时,应该在默认路径下,生成了两个文件,一个是hexo_rsa,另一个是hexo_rsa.pub;二、将SSH密钥添加到SSH-Agent在将新的SSH密钥添加到SSH-Agent管理密钥之前,您应该检查现有的SSH密钥。ls -al / .ssh看下返回的结果中是否已经存在了.pub结尾的文件,如果没有需要生成一个新的,如果存在了,直接看将SSH密钥配置到Github账户;6、启动ssh-agent,执行命令:eval $(ssh-agent -s)返回agent的Pid:7、将SSH密钥添加到SSH-Agent,其中hexo_rsa是生成SSH设置的名称ssh-ADD /.ssh/hexo_rsa8、打开hexo_rsa.pub文件,复制其内容,然后打开Github账户,按图片步骤设置,最后将复制内容放置Key内容中,设置title,点击添加即可。验证SSH链接是否成功:输入命令:ssh -T git@github.com你将会看到输入Yes就可以啦相关链接: https://help.github.com/artic… ...

February 1, 2019 · 1 min · jiezi

GitNote 基于 Git 的跨平台笔记软件正式发布

GitNote 基于 Git 的跨平台笔记软件为什么自从工作之后,我开始进行笔记记录,这是一个很棒的习惯.我曾经使用过 EDiary Evernote Onenote Wiz 麦库等,都是一些不错的笔记软件,但是都有一些各式各样的问题,不能满足我的使用.2013 年,我用 java 编写了第一款笔记软件 jnote,支持 markdown 和富文本编辑器,但是没有云同步功能.2016 年,我用 electron 和 JavaScript 编写了一个 markdown 编辑器 ndpeditor,不是笔记软件.2017 年,我用 electron 和 JavaScript 编写了基于 git 的 GitNote 笔记软件,这个采用一个 React开发的版本,这是一个没有发布的版本.2018 年,我用 Vue重构了 GitNote,更强大的 GitNote.git 同步git 是一个很棒的工具,GitNote 支持 git 的全部特性,并且不依赖本地 Git 环境. 你可以使用任何支持 Git 的仓库.https://github.com/ 免费版支持无限私有仓库https://BitBucket.com/ 免费版支持私有仓库https://gitlab.com/ 免费版支持私有仓库https://gitee.com/ 免费版支持私有仓库(推荐)https://coding.net/ 免费版支持私有仓库还有很多GitNote 是一款基于 Git 的跨平台笔记软件,内置 git 支持,无须本地有任何 git 环境,拥有 git 的全部特性,可以任意的恢复笔记版本记录,依托 github 的免费不限量私人仓库,你的笔记没有空间的限制,你的数据完全属于你自己.TODO轻量级的 todo 管理,在笔记中快速便捷创建 Todo ,没有复杂管理流程,只需要关注完成和未完成.用极简的方式来管理自己的 todo.富文本编辑器富文本编辑器,不仅支持各种复杂的文字编辑,还支持快捷键、公式、语法高亮、Todo、图片粘贴,等等功能.MARKDOWN不仅是一款漂亮的 markdown 的编辑器,而且还支持编写幻灯片功能,方便你进行幻灯片演讲.附件不仅仅支持各种各样的文件作为附件,还能自动识别图片,将图片插入到笔记中.跨平台Mac windows Linux 全平台支持,未来也会对移动端进行支持.收藏通过浏览器插件,可以收集网络上面的任何内容,自动同步到你的笔记仓库中.扩展提供强大丰富的扩展 API,可以自由的定制功能扩展插件,可以为提供思维脑图,番茄工作法,图床等等功能特色插件思维脑图支持思维脑图,帮助我们用形象化的方式,科学的处理事情,比如记录和激发创意,并且支持多种导出方式.流程图支持制作流程图,流程图,组织结构图,UML,ER 和网络拓扑图等,并且支持多种导出方式.演示文稿使用 markdown 可以很容易的创建出 web 版的演示文档,并支持导出.多图床支持多个图床平台上传,自动插入到笔记中,提供 API 可以自由定制自己的图床.官网地址:https://gitnoteapp.com/下载地址:https://gitnoteapp.com/zh/#do… ...

February 1, 2019 · 1 min · jiezi

【Github Pages】徒手实现分页

Github PagesGithub Pages 是 Github 免费提供的静态网站生成器,你可以利用其创建个人、企业、项目网站。其提供静态页面托管服务和一个二级域名,也可以绑定独立域名。可以很轻易的找到其介绍和如何构建自己的 Github Pages,就不详细介绍了可以参考这些内容What is GitHub Pages?Using Jekyll as a static site generator with GitHub Pages使用 github pages, 快速部署你的静态网页github pages搭建个人博客分页不管是个人博客还是其他主页,内容多了就需要分页展示文章列表。我们选择 Jekyll 作为页面生成器来管理页面。Jekyll 提供了分页功能,使用上也很方便。只需要 _config.yml 文件填加分页配置就可以直接用了。plugins: [jekyll-paginate]paginate: 20 # 每页文章数paginate_path: “essay/page:num” # 可选,分页链接然后在 index.html 直接写上如下代码就会自动生成分页目录<ul> {% for post in paginator.posts %} <li><a href="{{ post.url }}">{{ post.title }}</a></li> {% endfor %}</ul><nav class=“pagination” role=“navigation”> {% if paginator.previous_page %} <a class=“previous pagination__newer btn btn-small btn-tertiary” href="{{ paginator.previous_page_path }}">&larr; 上一页</a> {% endif %} <span class=“page_num pagination__page-number”>{{ paginator.page }} / {{ paginator.total_pages }}</span> {% if paginator.next_page %} <a class=“next pagination__older btn btn-small btn-tertiary” href="{{ paginator.next_page_path }}">下一页 &rarr;</a> {% endif %}</nav>jekyll 会自动生成如下目录效果如下非常方便,但是它也有很多明显的缺点。比如他只支持 _posts 目录下的文章进行自动生成,很大时候就不一定能满足需求了。自定义分页如上面所说,不想把所有文章都放到 _posts 一个目录。比如我想到放在独立的 blog 目录那怎么实现目录的自动生成和分页呢?借助 Data Files 和 PHP 来手动生成分页。_data/blogList.yml 定义如下列表 - key: 6a1b96bda21c937f01a7591ec3e84223 title: PHP实现一个轻量级容器 next: Travis CI 实现自动备份Segmentfault文章到Github - key: 13ee9e07ce28d6310eb5fec64404fa24 title: Travis CI 实现自动备份Segmentfault文章到Github prev: PHP实现一个轻量级容器 next: Travis CI 简介 - key: bb800b68ec4217869667407a8c1470f6 title: Travis CI 简介 prev: Travis CI 实现自动备份Segmentfault文章到Github next: 【php实现数据结构】链式队列在子目录另外定义一个 page.html 模板文件—layout: listtype: customListtitle: 我的博客page: 1total_pages: 100prev_page_path: nonenext_page_path: none—<p>同步自segmentfault(https://segmentfault.com/blog/actors315)</p><h2 id=“目录”>目录</h2><ul> {% for member in site.data.blogList limit:20 offset:#offset# %} <li><a href="/blog/markdown/{{ member.title }}">{{ member.title }}</a></li> {% endfor %}</ul>利用 data 的逻辑处理能力手动实现,然后 php 自动任务手动生成和 jekyll 自助目录同样的结构。$totalCount = count($list);$totalPage = ceil($totalCount / 20);for ($i = 1; $i <= $totalPage; $i++) { if ($i == 1) { $tempFile = DIR . “/../blog/index.html”; } else { $tempFile = DIR . “/../blog/page{$i}/index.html”; } $newPage = false; if (file_exists($tempFile)) { $tempContent = file_get_contents($tempFile); } else { $tempContent = file_get_contents(DIR . “/../blog/page.html”); $newPage = true; if (!is_dir($dir = dirname($tempFile))) { mkdir($dir, 0777, true); } } $tempContent = preg_replace(’/page:[\s]\d+[^\d]/’, “page: {$i}” . PHP_EOL, $tempContent); $tempContent = preg_replace(’/total_pages:[\s]\d+[^\d]/’, “total_pages: {$totalPage}” . PHP_EOL, $tempContent); if ($i == 2) { $tempContent = preg_replace(’/prev_page_path:[\s][^\s]+[\s]?/’, “prev_page_path: /blog/”, $tempContent); } elseif ($i > 2) { $prev = $i - 1; $tempContent = preg_replace(’/prev_page_path:[\s][^\s]+[\s]?/’, “prev_page_path: /blog/page{$prev}/”, $tempContent); } if ($i < $totalPage) { $next = $i + 1; $tempContent = preg_replace(’/next_page_path:[\s][^\s]+[\s]?/’, “next_page_path: /blog/page{$next}/”, $tempContent); } elseif ($i == $totalPage) { $tempContent = preg_replace(’/next_page_path:[\s][^\s]+[\s]?/’, “next_page_path: none”, $tempContent); } if ($newPage) { $tempContent = str_replace(’#offset#’, ($i - 1) * 20, $tempContent); } file_put_contents($tempFile, $tempContent);}这里需要配合后端代码,Github Pages 目前是不支持动态语言的,所以需要借助其他能力,可以参考之篇文章的介绍 《Travis CI 实现自动备份Segmentfault文章到Github》这样就徒手实现了一个分页功能,并且可以根据自己的需要随心所欲,你的分页你作主。详细实现可参考我的Github 页面 呜啦啦的碎碎念 ...

January 31, 2019 · 2 min · jiezi

关于git的仓库同步指南

关于git的仓库同步指南前言我们都知道,GitHub是一个方便多人协作的托管平台.如何将本地local仓库、个人远程origin仓库(GitHub上的仓库)和远程upstream仓库(在GitHub上fork别人的仓库)进行同步是多人协作的前提.那么我们就来看一下,过程该如何进行情景描述前提:A与B两人协作管理同一个upstream远程仓库,且本次操作A、B同时fork.场景一:现实中我们经常出现这种问题:A fork了远程upstream仓库,几天之后upstream仓库更新了新的版本,此时A的个人仓库与origin仓库已经落后,如果这个时候A 在落后的版本上继续commit,就会频频出错,那么A就需要更新本地仓库,在最新的基础上再操作.场景二:A在最新的版本上进行了自己的修改,并且已经push到upstream仓库,此时upstream版本已被A更新. B想要同步A的此次更新需要fetch远程upstream仓库(此时local 已更新),再将local 仓库push到本地origin仓库.完成同步的操作.图形详解实际操作场景一解决方法获取代码库1 、 fork别人的仓库到自己的GitHub2、将fork到的仓库clone到本地local同步更新代码因为fork并不能将所有东西都复制过来,这个操作只是获取到了路径,所以此时local仓库和upstream远程仓库并不同步,想要同步需先fetch(见操作3)3、使A local仓库和远程upstream仓库的master分支同步$ git fetch upstream$ git rebase upstream/master4、A在本地对代码进行修改之后,在SmartGit进行commit提交操作,然后push到自己的origin仓库$ git push origin master5、去GitHub提pr到此就已经完成了所有操作,如果原作者同意你的pr申请,你就成功的对upstream的代码进行了修改场景二解决方法6、B想要更新到upstream仓库的最新版本,就必须在终端进行fetch,rebase等操作(同上3)7、B的local仓库已同步,需要将最新版本push到B自己的origin仓库,使origin仓库更新结语好啦,到这里我们就能清楚的理解仓库同步的重要性以及如何同步各个仓库了.感谢阅读,下期再会. 原创作者:田晨晨、赵笑漫 日 期:2019年1月31日

January 31, 2019 · 1 min · jiezi

git 常用命令

github创建仓库到本地使用前期准备// 注册github账号// 地址:https://github.com/// 配置密匙// 地址:https://jingyan.baidu.com/article/414eccf6a330926b431f0ac6.html// 创建仓库git-test// git/https 切换到git模式(避免每次重复密匙验证)// 确保已经安装git bash,并打开 创建本地仓库// 在本地创建文件夹mkdir git-test// 复制代码,如以下示例,粘贴到git bash运行echo “# git-test” >> README.mdgit initgit add README.mdgit commit -m “first commit"git remote add origin git@github.com:webjimmylau/git-test.gitgit push -u origin master创建分支// 最好统一先在github上创建分支// 然后才在本地git pull分支git命令文件夹clear // 清空代码pwd // 显示当前目录mkdir fileName // 创建文件夹cd fileName // 进入文件夹git init // 把这个目录变成git可以管理的仓库文件echo xxx >> text.txt // 创建文件并更改或直接更改文件内容rm text.txt // 删除文件cat text.txt // 查看文件内容git diff text.txt // 查看文件修改的内容git diff HEAD – text.txt // 查看工作区和版本库里面最新版本的区别git reset HEAD . // 撤销件到暂存区git checkout – text.txt // 撤销修改文件内容 或 撤销删除文件分支git branch // 查看分支 *代表当前分支git branch dev // 创建分支git branch -d dev // 删除分支git branch -D dev // 强行删除分支git checkout dev // 切换分支git checkout -b dev // 创建并切换分支git checkout -b dev origin/dev // 创建并拉取远程分支dev并切换分支git merge dev // 在当前分支上合并dev内容git merge –no-ff -m ‘xxx’ dev // 合并分支,禁用‘Fast forward’模式,删除后还能保留分支信息版本查看git reflog // 查看全部版本(包括回滚后看不到的版本、误操作等)git log // 详细查看分支提交历史(当前版本的祖先版本)git log –pretty=oneline // 简单查看分支提交历史(去除作者和日期)git log –pretty=oneline –abbrev-commit // 查看精简分支提交历史(缩小版本号长度)git log –graph // 查看分支删除信息版本回滚git reset –hard HEAD^ // 回滚到上一个版本git reset –hard HEAD~100 // 回滚到上100个版本git reset –hard scd51f // 回滚到某版本拉取提交git clone git-test(url) // 克隆git-test项目git remote add origin git-test(url) // 关联一个远程库git pull origin master // 拉取成功 然后手动处理冲突git branch –set-upstream master // 解决分支冲突 git push -u origin master // 把本地master分支和远程master分支关联起来 // 并把本地master分支的最新修改推送到github上git push origin master // 把本地master分支的最新修改推送到github上git push origin :dev // 在远程分支上删除dev分支git remote // 查看远程库的信息 如:origingit remote -v // 查看远程库的详细信息 如:origin隐藏现场git stash // 将当前的工作现场隐藏起来git stash list // 查看隐藏的工作现场git stash apply // 恢复工作现场git stash drop // 删除隐藏的一条工作现场记录git stash pop // 恢复并删除提交到本地git status // 查看是否还有文件未提交git add text.txt // 添加文件到暂存区git commit -m ‘xxx’ // 把暂存区的文件提交到仓库git命令使用方法1、创建/修改/提交cd D: // 进入D盘mkdir www // 创建文件夹wwwcd www // 进入文件夹wwwmkdir git-test // 创建文件夹git-testcd git-test // 进入文件夹git-testpwd // 显示当前目录git init // 把这个目录变成git可以管理的仓库echo 111 >> readme.txt // 创建readme.txt文件 并添加内容111git add readme.txt // 添加readme.txt文件到暂存区里面去git commit -m “add file” // 把暂存区的文件提交到仓库git status // 查看是否还有文件未提交echo 222 >> readme.txt // 创建readme.txt文件并更改或直接更改readme.txt文件内容git status // 查看是否还有文件未提交git diff readme.txt // 查看readme.txt修改了什么内容git add readme.txt // 添加readme.txt文件到暂存区里面去git commit -m “change file” // 把暂存区里面的内容提交到仓库2、版本回退echo 333 >> readme.txt git add readme.txt git commit -m “change file"git log // 查看版本历史记录git log –pretty=oneline // 查看精简版本历史记录git reset –hard HEAD^ // 退回到上一个版本 ^上一个 ^^上两个 ~100上100个cat readme.txt // 查看readme.txt内容git log // 查看版本历史记录,被覆盖的最新版本333已经看不到,按q退出查询git reflog // 可以看到333内容的版本信息和对应的版本号git reset –hard xxxxxx // 恢复到333内容的版本cat readme.txt // 查看readme.txt内容3、理解工作区与暂存区的区别echo 444 >> readme.txt // 修改文件echo test >> test.txt // 新增文件git add readme.txt // 添加文件到暂存区git add test.txt // 添加文件到暂存区git commit -m “add files” // 把暂存区的文件提交到仓库4、撤销修改echo 555 >> readme.txt // 修改文件cat readme.txt // 查看文件git checkout – readme.txt // 把文件在工作区做的修改全部撤销git 666 >> readme.txt // 修改文件git add readme.txt // 添加文件到暂存区echo 777 >> readme.txt // 修改文件cat readme.txt // 查看文件git checkout – readme.txt // 把文件暂存区的修改保留,把工作区为修改全部撤销cat readme.txt // 666还有,777不见了5、删除文件echo b >> b.txt // 新建文件git add b.txt // 添加文件到暂存区git commit -m “change file” // 提交暂存区的文件到仓库rm b.txt // 删除文件git status // 查看文件状态git checkout – b.txt // 撤销删除文件6、远程仓库// 如何添加远程库git remote add origin git-test(url)git push -u origin master // 把本地master分支和远程master分支关联起来 // 并把本地master分支的最新修改推送到github上git push origin master // 把本地master分支的最新修改推送到github上// 如何从远程克隆git clone git-test(url)7、创建与合并分支git branch // 查看分支 *代表当前分支git branch dev // 创建分支git checkout dev // 切换分支git checkout -b dev // 创建并切换分支cat readme.txtecho 777 >> readme.txtcat readme.txtgit add readme.txtgit commit -m ‘xxx’git checkout mastercat readme.txt // 只有之前的内容666git merge dev // 在当前分支上合并dev内容cat readme.txt // master上多了内容777git branch -d dev // 删除dev分支// 如何解决冲突git merge fenzhi1 // 删除冲突,再提交// 分支管理策略 git merge –no-ff -m ’text’ dev // 合并分支,禁用‘Fast forward’模式,删除后还能保留分支信息git branch -d dev git log –graph –pretty=oneline –abbrev-commit // 查询被删除的分支信息8、bug分支git status // 有看到新增和修改的文件git stash // 将当前的工作现场隐藏起来git status // 隐藏之后,查看状态,是干净的git checkout -b issue-404 // 创建和切换分支到issue-404git add readme.txt // 把文件添加到暂存区git commit -m ‘fix some bug’ // 把暂存区的文件提交到仓库git checkout master // 切换分支到mastergit merge –no-ff -m ‘merge issue-404’ issue-404 // 合并issue-404分支cat readme.txt // 当前master分支可以看到issue-404分支的内容,完全一致git branch -d issue-404 // 在master分支上删除issue-404临时分支git checkout dev // 切换到dev分支git status // 查看状态,还是干净的git stash list // 查看隐藏的工作现场git stash apply // 恢复工作现场git stash drop // 删除隐藏的一条工作现场记录git stash pop // 恢复并删除9、多人协作git clone git-test(url) // 克隆git-test项目git pull origin master // 拉取master分支git push origin master // 推送master分支git pull origin dev // 拉取失败git branch –set-upstream dev // 解决分支冲突git pull origin dev // 拉取成功 然后手动处理冲突git push origin dev // 推送dev分支 ...

January 31, 2019 · 3 min · jiezi

Gitalk评论插件使用教程

Gitalk说明Gitalk 是一个基于 GitHub Issue 和 Preact 开发的评论插件。Gitalk 的特性:1、使用 GitHub 登录2、支持多语言 [en, zh-CN, zh-TW, es-ES, fr, ru]3、支持个人或组织4、无干扰模式(设置 distractionFreeMode 为 true 开启)5、快捷键提交评论 (cmd|ctrl + enter)使用Gitalk需要你做一些提前准备:1、在github上创建一个仓库,Gitalk会把评论放在这个仓库的issues里面。2、在github上申请一个GitHub Application,来让Gitalk有权限操作github上的仓库。2. 安装使用2.1 安装两种方式引入:1、直接在HTML文件中引入<link rel=“stylesheet” href=“https://unpkg.com/gitalk/dist/gitalk.css"><script src=“https://unpkg.com/gitalk/dist/gitalk.min.js"></script>建议直接把文件下载后放在项目目录中,这样就可以不再依赖第三方网络了。2、通过npm安装npm i –save gitalkimport ‘gitalk/dist/gitalk.css’import Gitalk from ‘gitalk'2.2 使用1、首先需要在html文件中添加一个容器,Gitalk组件会在此处显示<div id=“gitalk-container”></div>2、使用下面的JavaScript代码来生成Gitalk评论:var gitalk = new Gitalk({ clientID: ‘GitHub Application Client ID’, clientSecret: ‘GitHub Application Client Secret’, repo: ‘GitHub repo’, owner: ‘GitHub repo owner’, admin: [‘GitHub repo owner and collaborators, only these guys can initialize github issues’], id: location.pathname, // 确保唯一,并且长度小于50 distractionFreeMode: false // 类似Facebook评论框的全屏遮罩效果.})gitalk.render(‘gitalk-container’)里面参数下面会讲解.配置好后,页面最终效果(https://gitalk.github.io/):3. new Gitalk的参数说明new Gitalk的参数中有github的仓库信息和GitHub Application信息,所以首先我们创建这两个。首先你需要在github上创建一个仓库和申请一个GitHub Application。在github上创建一个仓库比较简单这里就不讲解了,下面讲一下如何申请一个GitHub Application注意:如果你打算在自己网站使用Gitalk,并且这个网站的源码在github的仓库上,那么你也可以直接使用这个仓库,Gitalk只使用仓库的Issues。3.1 申请一个GitHub Application参考资料Gitalk 官网Gitalk中文文档

January 31, 2019 · 1 min · jiezi

git 分支原理介绍

参考文章git-分支简介工作原理git 的工作原理:git 版本控制是通过保存不同时间点的快照实现的。git 在提交操作时,git 会保存一个提交对象,该提交对象中会包含:用户名邮箱提交信息父指针,自然的,首次提交的对象是没有父指针的,之后提交的对象才有父指针指向树对象的指针(树对象:git 通过计算每个子目录的校验和,将其保存为树对象)目录结构blob 对象索引git 的分支,其实本质上是指向提交对象的可变指针。由于创建分支的高效性,所以,git 鼓励开发人员创建分支!原理说明首先在创建 git 项目的时候,默认会创建 master 分支!这个 master 分支正常应该是指向最新一次提交。 head | master |one <- two <- thr假设这个时候,用户做了些修改,然后提交: head | master |one <- two <- thr <- four从上述两个过程可以看出,master 分支实际就是一个指向某次提交的指针!会根据用户的提交自动向前移动指向最新一次提交。如果用户新创建了一个分支(test)呢:git branch test他的实际结果就是如下: head | master |one <- two <- thr <- four | test因为这个时候用户在 master 分支下,用户再次做出修改提交: test |one <- two <- thr <- four <- five | master | head如果这个时候用户切换到 test 分支(git checkout test),做些修改然后再次提交: head | test | <- five-for-testone <- two <- thr <- four <- five | master产生了一个新的分支!! ...

January 30, 2019 · 1 min · jiezi

githubRank: Github 项目和用户排行榜

自己平时比较喜欢逛 Github,也热衷于发现各种各样神奇的仓库,所以干脆用官方的接口自己整合一下,便诞生了—— githubRank,这样一个展示仓库与用户排行的网站,虽然已经存在了很多基于官方接口的 github 项目排行的网站,但是给我的体验都不够友好和简洁。所以欢迎大家来体验一下 githubRank:http://zy2071.com/Fun/githubR…网页截图: (页面显示有一些缺陷有待后期优化)之前还做了一个 Github Trending 小程序,也欢迎扫码体验:Github Trending 小程序 ????

January 30, 2019 · 1 min · jiezi

开发函数计算的正确姿势——网页截图服务

前言首先介绍下在本文出现的几个比较重要的概念:函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息参考。Fun: Fun 是一个用于支持 Serverless 应用部署的工具,能帮助您便捷地管理函数计算、API 网关、日志服务等资源。它通过一个资源配置文件(template.yml),协助您进行开发、构建、部署操作。Fun 的更多文档参考。fun install: fun install 是 fun 工具的一个子命令,用于安装 pip 和 apt 依赖,提供了命令行接口和 fun.yml 描述文件两种形式。备注: 本文介绍的技巧需要 Fun 版本大于等于 2.9.3。依赖工具本项目是在 MacOS 下开发的,涉及到的工具是平台无关的,对于 Linux 和 Windows 桌面系统应该也同样适用。在开始本例之前请确保如下工具已经正确的安装,更新到最新版本,并进行正确的配置。DockerFunFcliFun 和 Fcli 工具依赖于 docker 来模拟本地环境。对于 MacOS 用户可以使用 homebrew 进行安装:brew cask install dockerbrew tap vangie/formulabrew install funbrew install fcliWindows 和 Linux 用户安装请参考:https://github.com/aliyun/fun/blob/master/docs/usage/installation.mdhttps://github.com/aliyun/fcli/releases安装好后,记得先执行 fun config 初始化一下配置。初始化使用 fun init 命令可以快捷的将本模板项目初始化到本地。$ fun init vangie/puppeteer-example? Please input oss bucket to upload chrome shell? chrome-headless? Please select a region? cn-hangzhou? Please input oss accessKeyId for upload? xxxxxxxxxxxKbBS? Please input oss accessKeySecret for upload? xxxxxxxxxxxx5ZgM上面会提示输入一个 OSS 的 BUCKET,注意 OSS Bucket 是全球唯一的,上面的 chrome-headless 已经被占用了,请换一个新的名称或者一个已经创建好的(已经创建好的,请确保 region 一致)。然后选择一个 OSS 的 Region,请保持和部署函数计算 Region 一致输入一个具备 OSS 写权限的秘钥。安装依赖$ fun installskip pulling image aliyunfc/runtime-nodejs8:build-1.2.0…Task => [UNNAMED] => apt-get update (if need) => apt-get install -y -d -o=dir::cache=/code/.fun/tmp libnss3 => bash -c ‘for f in $(ls /code/.fun/tmp/archives/*.deb); do dpkg -x $f /code/.fun/root; done;’ => bash -c ‘rm -rf /code/.fun/tmp/archives’Task => [UNNAMED] => bash -c ‘curl -L https://github.com/muxiangqiu/puppeteer-fc-starter-kit/raw/master/chrome/headless_shell.tar.gz –output headless_shell.tar.gz’…fun install 会执行 fun.yml 文件里的任务,这些任务会:安装 puppeteer 依赖的 .so 文件;将 puppeteer 依赖的 chrome headless 二进制文件上传到 OSS;安装 npm 依赖。部署$ fun deployusing region: cn-shanghaiusing accountId: ***********4733using accessKeyId: ***********KbBSusing timeout: 60Waiting for service puppeteer to be deployed… Waiting for function html2png to be deployed… Waiting for packaging function html2png code… package function html2png code done function html2png deploy successservice puppeteer deploy success执行$ fcli function invoke -s puppeteer -f html2pngThe screenshot has been uploaded to http://chrome-headless.oss-cn-shanghai.aliyuncs.com/screenshot.png打开上面的返回链接,看到截取出来的是全屏滚动的长图,考虑的篇幅下面只截取了部分:如果想换一个网址,可以使用如下命令格式fcli function invoke -s puppeteer -f html2png –event-str ‘http://www.alibaba.com’调试如果需要在本地调试代码,可以使用如下命令fun local invoke html2png <<<‘http://www.alibaba.com’参考阅读三分钟学会如何在函数计算中使用 puppeteer本文作者:倚贤阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

January 29, 2019 · 2 min · jiezi

SpringBoot实战 | 配置文件详解

微信公众号:一个优秀的废人如有问题或建议,请后台留言,我会尽力解决你的问题。前言如题,今天解析下 SpringBoot 的配置文件。自定义属性加载首先构建 SpringBoot 项目,不会的看这篇旧文 使用 IDEA 构建 Spring Boot 工程。首先在项目根目录下加入以下自定义属性:# 防止读取乱码spring.http.encoding.charset=UTF-8# 项目启动端口server.port=9999# 自定义配置com.nasus.author.name=一个优秀的废人com.nasus.article.title=SpringBoot配置文件详解使用 @value 注解读取配置文件属性:package com.nasus.bean;import lombok.Data;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;/** * Project Name:springboot_properties_demo <br/> * Package Name:com.nasus.properties <br/> * Date:2019/1/28 20:59 <br/> * <b>Description:</b> TODO: 描述该类的作用 <br/> * @author <a href=“turodog@foxmail.com”>nasus</a><br/> /@Data@Componentpublic class PropertiesBean { @Value("${com.nasus.author.name}") private String name; @Value("${com.nasus.article.title}") private String title; @Value("${com.nasus.doing}") private String desc;}之后新建 controller 测试自定义属性加载,代码如下:package com.nasus.controller;import com.nasus.bean.PropertiesBean;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/* * Project Name:springboot_properties_demo <br/> * Package Name:com.nasus.controller <br/> * Date:2019/1/28 21:41 <br/> * <b>Description:</b> TODO: 测试自定义属性加载<br/> * * @author <a href=“turodog@foxmail.com”>nasus</a><br/> */@RestController@RequestMapping("/test")public class TestController { @Autowired private PropertiesBean propertiesBean; @GetMapping("/getInfo") public PropertiesBean getInfo(){ return propertiesBean; }}访问 http://localhost:8080/test/getInfo 查看加载结果:可以看到,加入 @value 注解之后,配置文件的属性都被读取出来了。以前,或许我们还需要专门写一个读取配置文件的工具类才能把属性读取出来,现在有了 Spring ,我们可以直接使用 @value 就能读取了,简直不能太方便。本例源码在这:github 地址参数间的引用配置文件代码如下:# 防止读取乱码spring.http.encoding.charset=UTF-8# 项目启动端口server.port=9999# 自定义配置com.nasus.author.name=一个优秀的废人com.nasus.article.title=SpringBoot配置文件详解com.nasus.doing=${com.nasus.author.name}写文章《${com.nasus.article.title}》可以看到最后一个参数配置使用了前两个的参数配置,测试结果如下:使用随机数有时项目需求,可能我们需要配置一些随机数,比如说为了安全而随机配置的服务器端口,以及登录密钥。这时我们就可以用 SpringBoot 的 random 属性来配置随机数,比如:# 随机字符串com.nasus.article.value=${random.value}# 随机intcom.nasus.article.number=${random.int}# 随机longcom.nasus.article.bignumber=${random.long}# 10以内的随机数com.nasus.article.test1=${random.int(10)}# 10-20的随机数com.nasus.article.test2=${random.int[10,20]}使用多配置文件很多时候我们开发项目都需要很多套环境,比如有测试环境,开发环境以及生产环境。不同的环境就需要使用不同的配置文件,为此我们可以根据这 3 个环境分别新建 以下 3 个配置文件。application-dev.properties:开发环境application-test.properties:测试环境application-prod.properties:生产环境项目中默认的配置文件是 application.properties 。这时我们可以根据自己的环境去使用相应的配置文件,比如说,项目各个环境的端口必须不一样。那我们可以这样配置:application-dev.properties:开发环境server.port=6666application-test.properties:测试环境server.port=7777application-prod.properties:生产环境server.port=8888假如,现在我打包上线,那就必须用生产环境的配置文件了,这时我们可以在 默认的配置文件 application.properties 中加入以下配置即可spring.profiles.active=prod配置数据库SpringBoot 的配置文件有两种格式,一种是 .properties 格式(以上栗子都是用的这种)还有一种用的是 .yaml 格式。以下是用 yaml 方式配置。这两种格式并无好坏之分,纯看个人使用习惯。我就比较喜欢 yaml 格式,因为看起来比较简洁。spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=true username: 你的数据库名称 password: 你的数据库密码 jpa: hibernate: ddl-auto: update #ddl-auto:设为update 表示每次都重新建表 show-sql: true注意事项使用 yaml 格式需要注意一点就是 键值对冒号后面,必须空一格。application.properties 配置中文值的时候,读取出来的属性值会出现乱码问题。但是 application.yml 不会出现乱码问题。原因是,Spring Boot 是以 iso-8859 的编码方式读取 application.properties 配置文件。解决第二点,只需加入 spring.http.encoding.charset=UTF-8 配置即可。后语以上就是我对 SpringBoot 配置文件的理解与使用,当然以上只是介绍了一下 SpringBoot 配置文件的几个用法,它的用法还有非常多,想要深入使用还是需要各位多多深入实践。最后,对 Python 、Java 感兴趣请长按二维码关注一波,我会努力带给你们价值,如果觉得本文对你哪怕有一丁点帮助,请帮忙点好看,让更多人知道。另外,关注之后在发送 1024 可领取免费学习资料。资料内容详情请看这篇旧文:Python、C++、Java、Linux、Go、前端、算法资料分享 ...

January 29, 2019 · 1 min · jiezi

将Hexo同时部署在github和腾讯云开发者平台或Coding初级实践教程

写在前面的话其实我也是这两天才接触到Hexo,之前是用的wordpress在阿里云上挂着。觉得Hexo好像更符合现在我的审美,so, do it!嗯前面安装git和node.js我这边就省略掉了。作为一个爱搞事的,这些东西电脑上都有还有就是我照着网上的教程是没问题,但是走到一些页面的小功能的时候,就不起作用了,可能是版本更新不兼容了<!– more –>一. 安装Hexo,初始化npm install -g hexo全局安装Hexo 创建一个文件夹如blog,不用进去(可以用hexo -v检验是否安装成功)hexo init blog 初始化这个blog和文件夹名字要一样,否则又创建个新的npm install安装所需要的依赖后面就 hexo s -g 就是发布之前先生成静态文件 ,s:server,g:generate,访问下localhost:4000看ok不(不起作用,提示什么hexo <commands> 什么东西了,就进到blog的目录下,使用hexo命令)应该没有5了,如果上面没成功,那你去搜搜别人的初始化都怎么弄的,然后再回来看我剩下的实践二. 创建github公开库有个point就是创建Repository的名字格式是 username.github.io,(看到有的博主只用的username就行,你可以尝试一下,不行的话删了就行)比如我的是 dasnnj.github.io,是为了能生成page服务图片描述两步,输入Repository name,然后点击 create repository 按钮图片描述建错删除的话,点进去新建的库,点击setting,点击最下面的删除,需要输入库的名字才能确认删除图片描述没问题的话,还是要点进去setting,往下面滑动到GitHub Pages标题下面,照着那个链接点进去,不出意外就能直接访问到你的这个repository三. 创建腾讯云开发者平台(或Coding)公开库项目地址格式是 username.coding.me,格式不对会404哦,项目名称随便,确定就ok图片描述创建完记得进入代码浏览,初始化一下项目,添加一个readme文档就行了进入page服务,然后开启图片描述四. 配置服务并将文件部署到Github复制上面创建的两个库的git地址修改最下面的deploy,格式类似我这样的# Deployment## Docs: https://hexo.io/docs/deployment.htmldeploy: type: git repo: github: https://github.com/dasnnj/dasnnj.github.io.git,master coding: https://git.dev.tencent.com/dasnnj/dasnnj.coding.me.git,master # 腾讯 # coding: https://git.coding.net/dasnnj/dasnnj.coding.me.git,master # Coding执行hexo clean && hexo g && hexo s 清除缓存,生成静态文件,本地发布页面上没问题的话,就可以执行hexo d会弹出输入github账号密码,和腾讯开发者平台的账号密码。我的只输了一次,可能是我安装了tortoiseGit的原因?好像后面自动给我创建了私钥公钥部署成功,按照各自平台的pages服务提示的网址即可访问五. 其他配置(目前都是关于博客根目录下面的_config.yaml的修改)博客标题title: life is love # 主标题subtitle: 记录生活和学习 # 副标题description: Nothing is impossible, the word itself says I’m possible. # 个人描述keywords: author: Dasnnj # 用户language: zh-CN # 语言,不填默认英文timezone: Asia/Shanghai # 时区urlurl: / #这里如果你只部署了一个平台,那么填那个平台的地址,或者/都行,如果你部署在了两个平台上,那么就只写/吧root: /permalink: :year/:month/:day/:title/ # 链接格式https://newblog.dasnnj.cn/2019/01/26/标题名字/# 也可设置为根据 category/:title/ 分类/标题名字 # category/:title.html会在标题名字后面加上.htmlpermalink_defaults:时间格式date_format: YYYY-MM-DD HH:mm:ss time_format: HH:mm:ss这里给date加上小时分钟等,是为了解决新建页面,发表时间只显示日期没有时间其他# Directory source_dir: source #资源文件夹,这个文件夹用来存放内容public_dir: public #公共文件夹,这个文件夹用于存放生成的站点文件。tag_dir: tags # 标签文件夹 archive_dir: archives #归档文件夹category_dir: categories #分类文件夹code_dir: downloads/code #Include code 文件夹i18n_dir: :lang #国际化(i18n)文件夹skip_render: #跳过指定文件的渲染,您可使用 glob 表达式来匹配路径。 # Writingnew_post_name: :title.md # 新文章的文件名称default_layout: post #预设布局titlecase: false # 把标题转换为 title caseexternal_link: true # 在新标签中打开链接filename_case: 0 #把文件名称转换为 (1) 小写或 (2) 大写render_drafts: false #是否显示草稿post_asset_folder: false #是否启动 Asset 文件夹relative_link: false #把链接改为与根目录的相对位址 future: true #显示未来的文章highlight: #内容中代码块的设置 enable: true line_number: true auto_detect: false tab_replace:新建文章模板的key对应的含义属性 描述title 标题``slug 网址``layout 布局。默认为 default_layout 参数。``path 路径。默认会根据 new_post_path 参数创建文章路径。date 日期。默认为当前时间。我这篇文章的信息title: 将Hexo同时部署在github和腾讯云开发者平台或Coding初级实践教程date: 2019-01-26 20:52:03tags: [Hexo,github,coding] # 标签categories: - tech # 分类持续更新,下面大概要写我的next主题的一些配置,没有网上的大佬那样很全,但是对我来说很足够了(可能是版本不同,网上大佬的有部分可能不适用现在的,我这边会给出我的解决方法)##### 参考hexo的目录结构 - 一直玩编程官方文档 [1]: /img/bVbnQH4 [2]: /img/bVbnQH7 [3]: /img/bVbnQIc [4]: /img/bVbnQId ...

January 28, 2019 · 1 min · jiezi

[译]PEP 342--增强型生成器:协程

PEP原文 : https://www.python.org/dev/pe...PEP标题: Coroutines via Enhanced GeneratorsPEP作者: Guido van Rossum, Phillip J. Eby创建日期: 2005-05-10合入版本: 2.5译者 :豌豆花下猫(Python猫 公众号作者)目录简介动机规格摘要规格:将值发送到生成器新的生成器方法:send(value)新的语法:yield 表达式规格:异常和清理新语法:yield 允许在try-finally中新的生成器方法:throw(type,value = None,traceback = None)新的标准异常:GeneratorExit新的生成器方法:close()新的生成器方法:del()可选的扩展扩展的 continue 表达式未决问题示例参考实现致谢参考文献版权简介这个 PEP 在生成器的 API 和语法方面,提出了一些增强功能,使得它们可以作为简单的协程使用。这基本上是将下述两个 PEP 的想法结合起来,如果它被采纳,那它们就是多余的了:PEP-288,关于生成器的属性特征与异常(Attributes and Exceptions)。当前 PEP 沿用了它的下半部分,即生成器的异常(事实上,throw() 的方法名就取自 PEP-288)。PEP-342 用 yield 表达式(这个概念来自 PEP-288 的早期版本)来替换了生成器的属性特征。PEP-325,生成器支持释放资源。PEP-342 收紧了 PEP-325 中的一些松散的规范,使其更适用于实际的实现。(译注:PEP-288 和 PEP-325 都没有被采纳通过,它们的核心内容被集成到了 PEP-342里。)动机协程是表达许多算法的自然方式,例如模拟/仿真、游戏、异步 I/O、以及其它事件驱动编程或协同的多任务处理。Python 的生成器函数几乎就是协程——但不完全是——因为它们允许暂停来生成值,但又不允许在程序恢复时传入值或异常。它们也不允许在 try-finally 结构的 try 部分作暂停,因此很难令一个异常退出的(aborted)协程来清理自己。同样地,当其它函数在执行时,生成器不能提供控制,除非这些函数本身是生成器,并且外部生成器之所以写了去 yield,是要为了响应内部生成器所 yield 的值。这使得即使是相对简单的实现(如异步通信)也变得复杂,因为调用任意函数,要么需要生成器变堵塞(block,即无法提供控制),要么必须在每个要调用的函数的周围,添加一大堆引用循环代码(a lot of boilerplate looping code)。但是,如果有可能在生成器挂起的点上传递进来值或者异常,那么,一个简单的协程调度器或蹦床函数(trampoline function)就能使协程相互调用且不用阻塞——对异步应用程序有巨大好处。这些应用程序可以编写协程来运行非阻塞的 socket I/O,通过给 I/O 调度器提供控制,直到数据被发送或变为可用。同时,执行 I/O 的代码只需像如下方式操作,就能暂停执行,直到 nonblocking_read() 继续产生一个值:data = (yield nonblocking_read(my_socket, nbytes))换句话说, 通过给语言和生成器类型增加一些相对较小的增强,Python 不需要为整个程序编写一系列回调,就能支持异步操作,并且对于本该需要数百上千个协作式的多任务伪线程的(co-operatively multitasking pseudothreads)程序,也可以不需要使用资源密集型线程。因此,这些增强功能将给标准 Python 带来 Stackless Python 的许多优点,又无需对 CPython 核心及其 API 进行任何重大的修改。此外,这些增强在任何已经支持生成器的 Python 实现(例如 Jython)上都是可落实的。规格摘要通过给生成器类型增加一些简单的方法,以及两个微小的语法调整,Python 开发者就能够使用生成器函数来实现协程与其它的协作式多任务。这些方法和调整是:重定义 yield 为表达式(expression),而不是语句(statement)。当前的 yield 语句将变成一个 yield 表达式,其值将被丢弃。每当通过正常的 next() 调用来恢复生成器时,yield 表达式的返回值是 None。为生成器(generator-iterator)添加一个新的 send() 方法,它会恢复生成器,并且 send 一个值作为当前表达式的结果。send() 方法返回的是生成器产生的 next 值,若生成器没有产生值就退出的话,则抛出 StopIteration 。为生成器(generator-iterator)添加一个新的 throw() 方法,它在生成器暂停处抛出异常,并返回生成器产生的下一个值,若生成器没有产生值就退出的话,则抛出 StopIteration (如果生成器没有捕获传入的异常,或者它引发了其它异常,则该异常会传递给调用者。)为生成器(generator-iterator)添加一个新的 close() 方法,它在生成器暂停处引发 GeneratorExit 。如果生成器在之后引发 StopIteration (通过正常退出,或者已经被关闭)或 GeneratorExit (通过不捕获异常),则 close() 返回给其调用者。如果生成器产生一个值,则抛出 RuntimeError。如果生成器引发任何其它异常,也会传递给调用者。如果生成器已经退出(异常退出或正常退出),则 close() 不执行任何操作。增加了支持,确保即使在生成器被垃圾回收时,也会调用 close()。允许 yield 在 try-finally 块中使用,因为现在允许在 finally 语句中执行垃圾回收或显式地调用 close() 。实现了所有这些变更的原型补丁已经可用了,可作为当前 Python CVS HEAD 的 SourceForge 补丁。# 1223381设计规格:将值发送进生成器新的生成器方法:send(value)为生成器提出了一种新的方法,即 send() 。它只接收一个参数,并将它发送给生成器。调用 send(None) 完全等同于调用生成器的 next() 方法。使用其它参数调用 send() 也有同样的效果,不同的是,当前生成器表达式产生的值会不一样。因为生成器在生成器函数体的头部执行,所以在刚刚创建生成器时不会有 yield 表达式来接收值,因此,当生成器刚启动时,禁止使用非 None 参数来调用 send() ,如果调用了,就会抛出 TypeError (可能是由于某种逻辑错误)。所以,在与协程通信前,必须先调用 next() 或 send(None) ,来将程序推进到第一个 yield 表达式。与 next() 方法一样,send() 方法也返回生成器产生的下一个值,或者抛出 StopIteration 异常(当生成器正常退出,或早已退出时)。如果生成器出现未捕获的异常,则它会传给调用者。新语法:yield 表达式yield 语句(yield-statement)可以被用在赋值表达式的右侧;在这种情况下,它就是 yield 表达式(yield-expression)。除非使用非 None 参数调用 send() ,否则 yield 表达式的值就是 None。见下文。yield 表达式必须始终用括号括起来,除非它是作为顶级表达式而出现在赋值表达式的右侧。所以,下面例子都是合法的:x = yield 42x = yieldx = 12 + (yield 42)x = 12 + (yield)foo(yield 42)foo(yield)而下面的例子则是非法的(举了一些特例的原因是,当前的 yield 12,42 是合法的):x = 12 + yield 42x = 12 + yieldfoo(yield 42, 12)foo(yield, 12)请注意,如今没有表达式的 yield-语句 和 yield-表达式是合法的。这意味着:当 next() 调用中的信息流被反转时,应该可以在不传递显式的值的情况下 yield (yield 当然就等同于 yield None)。当调用 send(value) 时,它恢复的 yield 表达式将返回传入的值。当调用 next() 时,它恢复的 yield 表达式将返回 None。如果 yield-表达式(yield-expression)是一个 yield-语句(yield-statement),其返回值会被忽略,就类似于忽略用作语句的函数的返回值。实际上,yield 表达式就像一个反函数调用(inverted function);它所 yield 的值实际上是当前函数返回(生成)的,而它 return 的值则是通过 send() 传入的参数。提示:这样的拓展语法,使得它非常地接近于 Ruby。这是故意的。请注意,Python 在阻塞时,通过使用 send(EXPR) 而不是 return EXPR 来传值给生成器,并且在生成器与阻塞之间传递控制权的底层机制完全不同。Python 中的阻塞不会被编译成 thunk,相反,yield 暂停生成器的执行进度。有一些不是这样的特例,在 Python 中,你不能保存阻塞以供后续调用,并且你无法测试是否存在着阻塞。(XXX - 关于阻塞的这些东西似乎不合适,或许 Guido 会编辑下,做澄清。)设计规格:异常和清理让生成器对象成为通过调用生成器函数而生成的迭代器。本节中的 g 指的都是生成器对象。新语法:yield 允许在 try-finally 里生成器函数的语法被拓展了,允许在 try-finally 语句中使用 yield 语句。新的生成器方法:throw(type,value = None,traceback = None)g.throw(type, value, traceback) 会使生成器在挂起的点处抛出指定的异常(即在 yield 语句中,或在其函数体的头部、且还未调用 next() 时)。如果生成器捕获了异常,并生成了新的值,则它就是 g.throw() 的返回值。如果生成器没有捕获异常,那 throw() 也会抛出同样的异常(它溜走了)。如果生成器抛出其它异常(包括返回时产生的 StopIteration),那该异常会被 throw() 抛出。总之,throw() 的行为类似于 next() 或 send(),除了它是在挂起点处抛出异常。如果生成器已经处于关闭状态,throw() 只会抛出经过它的异常,而不去执行生成器的任何代码。抛出异常的效果完全像它所声明的那样:raise type, value, traceback会在暂停点执行。type 参数不能是 None,且 type 与 value 的类型必须得兼容。如果 value 不是 type 的实例(instance),则按照 raise 语句创建异常实例的规则,用 value 来生成新的异常实例。如果提供了 traceback 参数,则它必须是有效的 Python 堆栈(traceback)对象,否则会抛出 TypeError 。注释:选择 throw() 这个名称,有几个原因。Raise 是一个关键字,因此不能作为方法的名称。与 raise 不同(它在执行点处即时地抛出异常),throw() 首先恢复生成器,然后才抛出异常。单词 throw 意味着将异常抛在别处,并且跟其它语言相关联。考虑了几个替代的方法名:resolve(), signal(), genraise(), raiseinto() 和 flush() 。没有一个像 throw() 那般合适。新的标准异常:GeneratorExit定义了一个新的标准异常 GeneratorExit,继承自 Exception。生成器应该继续抛出它(或者就不捕获它),或者通过抛出 StopIteration 来处理这个问题。新的生成器方法:close()g.close() 由以下伪代码定义:def close(self): try: self.throw(GeneratorExit) except (GeneratorExit, StopIteration): pass else: raise RuntimeError(“generator ignored GeneratorExit”) # Other exceptions are not caught新的生成器方法:del()g.__ del __() 是 g.close() 的装饰器。当生成器对象被作垃圾回收时,会调用它(在 CPython 中,则是它的引用计数变为零时)。如果 close() 引发异常, 异常的堆栈信息(traceback)会被打印到 sys.stderr 并被忽略掉;它不会退回到触发垃圾回收的地方。这与类实例在处理 del()的异常时的方法一样。如果生成器对象被循环引用,则可能不会调用 g.del() 。这是当前 CPython 的垃圾收集器的表现。做此限制的原因是,GC 代码需要在一个任意点打破循环,以便回收它,在此之后,不允许 Python 代码“看到”形成循环的对象,因为它们可能处于无效的状态。被用于解开(hanging off)循环的对象不受此限制。尽管实际上不太可能看到生成器被循环引用。但是,若将生成器对象存储在全局变量中,则会通过生成器框架的 f_globals 指针创建一个循环。另外,若在数据结构中存储对生成器对象的引用,且该数据结构被作为参数传递给生成器,这也会创造一个循环引用(例如,如果一个对象具有一个作为生成器的方法,并持有由该方法创建的运行中的迭代器的引用)。鉴于生成器的典型用法,这些情况都不太可能。此外,CPython 在实现当前 PEP 时,每当由于错误或正常退出而终止执行时,会释放被生成器使用的框架对象(frame object)。这保证了那些无法被恢复的生成器不会成为无法回收的循环引用的部分。这就允许了其它代码在 try-finally 或 with 语句中使用 close() (参考 PEP-343),确保了给定的生成器会正确地完结。可选扩展扩展的 continue 语句本 PEP 的早期草案提出了一种新的 continue EXPR 语法,用于 for 循环(继承自 PEP-340),将 EXPR 的值传给被遍历的迭代器。此功能暂时被撤销了,因为本 PEP 的范围已经缩小,只关注将值传给生成器迭代器(generator-iterator),而非其它类型的迭代器。Python-Dev 邮件列表中的一些人也觉得为这个特定功能添加新语法是为时过早(would be premature at best)。未决问题Python-Dev 邮件的讨论提出了一些未决的问题。我罗列于此,附上我推荐的解决方案与它的动机。目前编写的 PEP 也反映了这种喜好的解决方案。当生成器产生另一个值作为对“GeneratorExit”异常的响应时,close()应该引发什么异常?我最初选择了 TypeError ,因为它表示生成器函数发生了严重的错误行为,应该通过修改代码来修复。但是 PEP-343 中的 with_template 装饰器类使用了 RuntimeError 来进行类似处理。可以说它们都应该使用相同的异常。我宁愿不为此目的引入新的异常类,因为它不是我希望人们捕获的异常:我希望它变成一个 traceback 给程序员看到,然后进行修复。所以我觉得它们都应该抛出 RuntimeError 。有一些先例:在检测到无限递归的情况下,或者检测到未初始化的对象(由于各种各样的原因),核心 Python 代码会抛出该异常。Oren Tirosh 建议将 send() 方法重命名为 feed() ,以便能跟 consumer 接口兼容(规范参见:http://effbot.org/zone/consumer.htm)。然而,仔细观察 consumer 接口,似乎 feed() 所需的语义与 send() 不同,因为后者不能在刚启动的生成器上作有意义的调用。此外,当前定义的 consumer 接口不包含对 StopIteration 的处理。因此,创建一个贴合 consumer 接口的简单的装饰器,来装饰生成器函数,似乎会更有用。举个例子,它可以用初始的 next() 调用给生成器预热(warm up),追踪 StopIteration,甚至可以通过重新调用生成器来提供 reset() 用途。示例一个简单的 consumer 装饰器,它使生成器函数在最初调用时,就自动地前进到第一个 yield 点:def consumer(func): def wrapper(args,**kw): gen = func(args, **kw) gen.next() return gen wrapper.name = func.name wrapper.dict = func.dict wrapper.doc = func.doc return wrapper一个使用 consumer 装饰器创建反向生成器(reverse generator)的示例,该生成器接收图像并创建缩略图,再发送给其它 consumer。像这样的函数可以链接在一起,形成 consumer 间的高效处理流水线,且每个流水线都可以具有复杂的内部状态:@consumerdef thumbnail_pager(pagesize, thumbsize, destination): while True: page = new_image(pagesize) rows, columns = pagesize / thumbsize pending = False try: for row in xrange(rows): for column in xrange(columns): thumb = create_thumbnail((yield), thumbsize) page.write( thumb, colthumbsize.x, rowthumbsize.y ) pending = True except GeneratorExit: # close() was called, so flush any pending output if pending: destination.send(page) # then close the downstream consumer, and exit destination.close() return else: # we finished a page full of thumbnails, so send it # downstream and keep on looping destination.send(page)@consumerdef jpeg_writer(dirname): fileno = 1 while True: filename = os.path.join(dirname,“page%04d.jpg” % fileno) write_jpeg((yield), filename) fileno += 1# Put them together to make a function that makes thumbnail# pages from a list of images and other parameters.#def write_thumbnails(pagesize, thumbsize, images, output_dir): pipeline = thumbnail_pager( pagesize, thumbsize, jpeg_writer(output_dir) ) for image in images: pipeline.send(image) pipeline.close()一个简单的协程调度器或蹦床(trampoline),它允许协程通过 yield 其它协程,来调用后者。被调用的协程所产生的非生成器的值,会被返回给调用方的协程。类似地,如果被调用的协程抛出异常,该异常也会传导给调用者。实际上,只要你用 yield 表达式来调用协程(否则会阻塞),这个例子就模拟了 Stackless Python 中使用的简单的子任务(tasklet)。这只是一个非常简单的例子,但也可以使用更复杂的调度程序。(例如,现有的 GTasklet 框架 (http://www.gnome.org/~gjc/gtasklet/gtasklets.html) 和 peak.events 框架 (http://peak.telecommunity.com/) 已经实现类似的调度功能,但大多数因为无法将值或异常传给生成器,而必须使用很尴尬的解决方法。)import collectionsclass Trampoline: “““Manage communications between coroutines””” running = False def init(self): self.queue = collections.deque() def add(self, coroutine): “““Request that a coroutine be executed””” self.schedule(coroutine) def run(self): result = None self.running = True try: while self.running and self.queue: func = self.queue.popleft() result = func() return result finally: self.running = False def stop(self): self.running = False def schedule(self, coroutine, stack=(), val=None, *exc): def resume(): value = val try: if exc: value = coroutine.throw(value,*exc) else: value = coroutine.send(value) except: if stack: # send the error back to the “caller” self.schedule( stack[0], stack[1], *sys.exc_info() ) else: # Nothing left in this pseudothread to # handle it, let it propagate to the # run loop raise if isinstance(value, types.GeneratorType): # Yielded to a specific coroutine, push the # current one on the stack, and call the new # one with no args self.schedule(value, (coroutine,stack)) elif stack: # Yielded a result, pop the stack and send the # value to the caller self.schedule(stack[0], stack[1], value) # else: this pseudothread has ended self.queue.append(resume)一个简单的 echo 服务器以及用蹦床原理实现的运行代码(假设存在 nonblocking_read 、nonblocking_write 和其它 I/O 协程,该例子在连接关闭时抛出 ConnectionLost ):# coroutine function that echos data back on a connected# socket#def echo_handler(sock): while True: try: data = yield nonblocking_read(sock) yield nonblocking_write(sock, data) except ConnectionLost: pass # exit normally if connection lost# coroutine function that listens for connections on a# socket, and then launches a service “handler” coroutine# to service the connection#def listen_on(trampoline, sock, handler): while True: # get the next incoming connection connected_socket = yield nonblocking_accept(sock) # start another coroutine to handle the connection trampoline.add( handler(connected_socket) )# Create a scheduler to manage all our coroutinest = Trampoline()# Create a coroutine instance to run the echo_handler on# incoming connections#server = listen_on( t, listening_socket(“localhost”,“echo”), echo_handler)# Add the coroutine to the schedulert.add(server)# loop forever, accepting connections and servicing them# “in parallel”#t.run()参考实现实现了本 PEP 中描述的所有功能的原型补丁已经可用,参见 SourceForge 补丁 1223381 (https://bugs.python.org/issue1223381)。该补丁已提交到 CVS,2005年8月 01-02。致谢Raymond Hettinger (PEP 288) 与 Samuele Pedroni (PEP 325) 第一个正式地提出将值或异常传递给生成器的想法,以及关闭生成器的能力。Timothy Delaney 建议了本 PEP 的标题,还有 Steven Bethard 帮忙编辑了早期的版本。另见 PEP-340 的致谢部分。参考文献TBD.版权本文档已经放置在公共领域。源文档:https://github.com/python/peps/blob/master/pep-0342.txt—————-(译文完)——————–相关链接: PEP背景知识 :学习Python,怎能不懂点PEP呢?PEP翻译计划 :https://github.com/chinesehua…[[译] PEP 255–简单的生成器](https://mp.weixin.qq.com/s/vj...[[译]PEP 525–异步生成器](https://mp.weixin.qq.com/s/fy…花下猫语: 唠叨几句吧,年前这几周事情太多了,挤着时间好歹是又翻译出一篇 PEP。与生成器密切相关的 PEP 已经完成 3/4,年后再译最后一篇(PEP-380)。当初翻译第一篇,完全是一时兴起,直觉这是一件有意义的事,现在呢,这个念头开始有点膨胀——我竟然在 Github 上建了个翻译项目。我深知,自己水平实在有限,因此不求得到多少认同吧。但行好事,莫问前程。不过,若有人帮着吆喝一声,也是极好的。 —————–本文原创并首发于微信公众号【Python猫】,后台回复“爱学习”,免费获得20+本精选电子书。 ...

January 27, 2019 · 5 min · jiezi

Gitter - 高颜值GitHub小程序客户端诞生记

前言嗯,可能一进来大部分人都会觉得,为什么还会有人重复造轮子,GitHub第三方客户端都已经烂大街啦。确实,一开始我自己也是这么觉得的,也问过自己是否真的有意义再去做这样一个项目。思考再三,以下原因也决定了我愿意去做一个让自己满意的GitHub第三方客户端。对于时常关注GitHub Trending列表的笔者来说,迫切需要一个更简单的方式随时随地去跟随GitHub最新的技术潮流;已有的一些GitHub小程序客户端颜值与功能并不能满足笔者的要求;听说iOS开发没人要了,掌握一门新的开发技能,又何尝不可?其实也没那么多原因,既然想做,那就去做,开心最重要。1. GitterGitHub:https://github.com/huangjiank…,可能是目前颜值最高的GitHub小程序客户端,欢迎star数据来源:GitHub API v3目前实现的功能有:实时查看Trending显示用户列表仓库和用户的搜索仓库:详情展示、README.md展示、Star/Unstar、Fork、Contributors展示、查看仓库文件内容开发者:Follow/Unfollow、显示用户的followers/followingIssue:查看issue列表、新增issue、新增issue评论分享仓库、开发者…Gitter的初衷并不是想把网页端所有功能照搬到小程序上,因为那样的体验并不会很友好,比如说,笔者自己也不想在手机上阅读代码,那将会是一件很痛苦的事。在保证用户体验的前提下,让用户用更简单的方式得到自己想要的,这是一件有趣的事。2. 探索篇技术选型第一次觉得,在茫茫前端的世界里,自己是那么渺小。当决定去做这个项目的时候,就开始了马不停蹄的技术选型,但摆在自己面前的选择是那么的多,也不得不感慨,前端的世界,真的很精彩。原生开发:基本上一开始就放弃了,开发体验很不友好;WePY:之前用这个框架已经开发过一个小程序,诗词墨客,不得不说,坑是真多,用过的都知道;mpvue:用Vue的方式去开发小程序,个人觉得文档并不是很齐全,加上近期维护比较少,可能是趋于稳定了?Taro:用React的方式去开发小程序,Taro团队的小伙伴维护真的很勤快,也很耐心的解答大家疑问,文档也比较齐全,开发体验也很棒,还可以一键生成多端运行的代码(暂没尝试)货比三家,经过一段时间的尝试及采坑,综合自己目前的能力,最终确定了Gitter的技术选型:Taro + Taro UI + Redux + 云开发 Node.js页面设计其实,作为一名Coder,曾经一直想找个UI设计师妹子做老婆的(肯定有和我一样想法的Coder),多搭配啊。现在想想,code不是生活的全部,现在的我一样很幸福。话回正题,没有设计师老婆页面设计怎么办?毕竟笔者想要的是一款高颜值的GitHub小程序。嗯,不慌,默默的拿出了笔者沉寂已久的Photoshop和Sketch。不敢说自己的设计能力如何,Gitter的设计至少是能让笔者自己心情愉悦的,倘若哪位设计爱好者想对Gitter的设计进行改良,欢迎欢迎,十二分的欢迎!3. 开发篇Talk is cheap. Show me the code.作为一篇技术性文章,怎可能少得了代码。在这里主要写写几个采坑点,作为一个前端小白,相信各位读者均是笔者的前辈,还望多多指教!Trending进入开发阶段没多久,就遇到了第一个坑。GitHub居然没有提供Trending列表的API!!!也没有过多的去想GitHub为什么不提供这个API,只想着怎么去尽快填好这个坑。一开始尝试使用Scrapy写一个爬虫对网页端的Trending列表信息进行定时爬取及存储供小程序端使用,但最终还是放弃了这个做法,因为笔者并没有服务器与已经备案好的域名,小程序的云开发也只支持Node.js的部署。开源的力量还是强大,最终找到了github-trending-api,稍作修改,成功部署到小程序云开发后台,在此,感谢原作者的努力。爬取Trending Repositoriesasync function fetchRepositories({ language = ‘’, since = ‘daily’,} = {}) { const url = ${GITHUB_URL}/trending/${language}?since=${since}; const data = await fetch(url); const $ = cheerio.load(await data.text()); return ( $(’.repo-list li’) .get() // eslint-disable-next-line complexity .map(repo => { const $repo = $(repo); const title = $repo .find(‘h3’) .text() .trim(); const relativeUrl = $repo .find(‘h3’) .find(‘a’) .attr(‘href’); const currentPeriodStarsString = $repo .find(’.float-sm-right’) .text() .trim() || /* istanbul ignore next / ‘’; const builtBy = $repo .find(‘span:contains(“Built by”)’) .parent() .find(’[data-hovercard-type=“user”]’) .map((i, user) => { const altString = $(user) .children(‘img’) .attr(‘alt’); const avatarUrl = $(user) .children(‘img’) .attr(‘src’); return { username: altString ? altString.slice(1) : / istanbul ignore next / null, href: ${GITHUB_URL}${user.attribs.href}, avatar: removeDefaultAvatarSize(avatarUrl), }; }) .get(); const colorNode = $repo.find(’.repo-language-color’); const langColor = colorNode.length ? colorNode.css(‘background-color’) : null; const langNode = $repo.find(’[itemprop=programmingLanguage]’); const lang = langNode.length ? langNode.text().trim() : / istanbul ignore next / null; return omitNil({ author: title.split(’ / ‘)[0], name: title.split(’ / ‘)[1], url: ${GITHUB_URL}${relativeUrl}, description: $repo .find(’.py-1 p’) .text() .trim() || / istanbul ignore next / ‘’, language: lang, languageColor: langColor, stars: parseInt( $repo .find([href="${relativeUrl}/stargazers"]) .text() .replace(’,’, ‘’) || / istanbul ignore next / 0, 10 ), forks: parseInt( $repo .find([href="${relativeUrl}/network"]) .text() .replace(’,’, ‘’) || / istanbul ignore next / 0, 10 ), currentPeriodStars: parseInt( currentPeriodStarsString.split(’ ‘)[0].replace(’,’, ‘’) || / istanbul ignore next / 0, 10 ), builtBy, }); }) );}爬取Trending Developersasync function fetchDevelopers({ language = ‘’, since = ‘daily’ } = {}) { const data = await fetch( ${GITHUB_URL}/trending/developers/${language}?since=${since} ); const $ = cheerio.load(await data.text()); return $(’.explore-content li’) .get() .map(dev => { const $dev = $(dev); const relativeUrl = $dev.find(’.f3 a’).attr(‘href’); const name = getMatchString( $dev .find(’.f3 a span’) .text() .trim(), /^((.+))$/i ); $dev.find(’.f3 a span’).remove(); const username = $dev .find(’.f3 a’) .text() .trim(); const $repo = $dev.find(’.repo-snipit’); return omitNil({ username, name, url: ${GITHUB_URL}${relativeUrl}, avatar: removeDefaultAvatarSize($dev.find(‘img’).attr(‘src’)), repo: { name: $repo .find(’.repo-snipit-name span.repo’) .text() .trim(), description: $repo .find(’.repo-snipit-description’) .text() .trim() || / istanbul ignore next */ ‘’, url: ${GITHUB_URL}${$repo.attr('href')}, }, }); });}Trending列表云函数exports.main = async (event, context) => { const { type, language, since } = event let res = null; let date = new Date() if (type === ‘repositories’) { const cacheKey = repositories::${language || 'nolang'}::${since || 'daily'}; const cacheData = await db.collection(‘repositories’).where({ cacheKey: cacheKey }).orderBy(‘cacheDate’, ‘desc’).get() if (cacheData.data.length !== 0 && ((date.getTime() - cacheData.data[0].cacheDate) < 1800 * 1000)) { res = JSON.parse(cacheData.data[0].content) } else { res = await fetchRepositories({ language, since }); await db.collection(‘repositories’).add({ data: { cacheDate: date.getTime(), cacheKey: cacheKey, content: JSON.stringify(res) } }) } } else if (type === ‘developers’) { const cacheKey = developers::${language || 'nolang'}::${since || 'daily'}; const cacheData = await db.collection(‘developers’).where({ cacheKey: cacheKey }).orderBy(‘cacheDate’, ‘desc’).get() if (cacheData.data.length !== 0 && ((date.getTime() - cacheData.data[0].cacheDate) < 1800 * 1000)) { res = JSON.parse(cacheData.data[0].content) } else { res = await fetchDevelopers({ language, since }); await db.collection(‘developers’).add({ data: { cacheDate: date.getTime(), cacheKey: cacheKey, content: JSON.stringify(res) } }) } } return { data: res }}Markdown解析嗯,这是一个大坑。在做技术调研的时候,发现小程序端Markdown解析主要有以下方案:wxParse:作者最后一次提交已是两年前了,经过自己的尝试,也确实发现已经不适合如README.md的解析wemark:一款很优秀的微信小程序Markdown渲染库,但经过笔者尝试之后,发现对README.md的解析并不完美towxml:目前发现是微信小程序最完美的Markdown渲染库,已经能近乎完美的对README.md进行解析并展示在Markdown解析这一块,最终采用的也是towxml,但发现在解析性能这一块,目前并不是很优秀,对一些比较大的数据解析也超出了小程序所能承受的范围,还好贴心的作者(sbfkcel)提供了服务端的支持,在此感谢作者的努力!最近这个项目????了,只能说跟不上年轻人的节奏了。Markdown解析云函数const Towxml = require(’towxml’);const towxml = new Towxml();// 云函数入口函数exports.main = async (event, context) => { const { func, type, content } = event let res if (func === ‘parse’) { if (type === ‘markdown’) { res = await towxml.toJson(content || ‘’, ‘markdown’); } else { res = await towxml.toJson(content || ‘’, ‘html’); } } return { data: res }}markdown.js组件import Taro, { Component } from ‘@tarojs/taro’import PropTypes from ‘prop-types’import { View, Text } from ‘@tarojs/components’import { AtActivityIndicator } from ’taro-ui’import ‘./markdown.less’import Towxml from ‘../towxml/main’const render = new Towxml()export default class Markdown extends Component { static propTypes = { md: PropTypes.string, base: PropTypes.string } static defaultProps = { md: null, base: null } constructor(props) { super(props) this.state = { data: null, fail: false } } componentDidMount() { this.parseReadme() } parseReadme() { const { md, base } = this.props let that = this wx.cloud.callFunction({ // 要调用的云函数名称 name: ‘parse’, // 传递给云函数的event参数 data: { func: ‘parse’, type: ‘markdown’, content: md, } }).then(res => { let data = res.result.data if (base && base.length > 0) { data = render.initData(data, {base: base, app: this.$scope}) } that.setState({ fail: false, data: data }) }).catch(err => { console.log(‘cloud’, err) that.setState({ fail: true }) }) } render() { const { data, fail } = this.state if (fail) { return ( <View className=‘fail’ onClick={this.parseReadme.bind(this)}> <Text className=‘text’>load failed, try it again?</Text> </View> ) } return ( <View> { data ? ( <View> <import src=’../towxml/entry.wxml’ /> <template is=‘entry’ data=’{{…data}}’ /> </View> ) : ( <View className=‘loading’> <AtActivityIndicator size={20} color=’#2d8cf0’ content=‘loading…’ /> </View> ) } </View> ) }}Redux其实,笔者在该项目中,对Redux的使用并不多。一开始,笔者觉得所有的接口请求都应该通过Redux操作,后面才发现,并不是所有的操作都必须使用Redux,最后,在本项目中,只有获取个人信息的时候使用了Redux。// 获取个人信息export const getUserInfo = createApiAction(USERINFO, (params) => api.get(’/user’, params))export function createApiAction(actionType, func = () => {}) { return ( params = {}, callback = { success: () => {}, failed: () => {} }, customActionType = actionType, ) => async (dispatch) => { try { dispatch({ type: ${customActionType }_request, params }); const data = await func(params); dispatch({ type: customActionType, params, payload: data }); callback.success && callback.success({ payload: data }) return data } catch (e) { dispatch({ type: ${customActionType }_failure, params, payload: e }) callback.failed && callback.failed({ payload: e }) } }}getUserInfo() { if (hasLogin()) { userAction.getUserInfo().then(()=>{ Taro.hideLoading() Taro.stopPullDownRefresh() }) } else { Taro.hideLoading() Taro.stopPullDownRefresh() }}const mapStateToProps = (state, ownProps) => { return { userInfo: state.user.userInfo }}export default connect(mapStateToProps)(Index)export default function user (state = INITIAL_STATE, action) { switch (action.type) { case USERINFO: return { …state, userInfo: action.payload.data } default: return state }}目前,笔者对Redux还是处于一知半解的状态,嗯,学习的路还很长。4. 结语篇当Gitter第一个版本通过审核的时候,心情是很激动的,就像自己的孩子一样,看着他一点一点的长大,笔者也很享受这样一个项目从无到有的过程,在此,对那些帮助过笔者的人一并表示感谢。当然,目前功能和体验上可能有些不大完善,也希望大家能提供一些宝贵的意见,Gitter走向完美的路上希望有你!最后,希望Gitter小程序能对你有所帮助!

January 26, 2019 · 5 min · jiezi

Github API v4: GraphQL

GraphQL势不可挡,有着即将取代REST的API架构。主要好处就是“你要什么,api就给你什么。而不是你要不要都给你返回一大堆没用的。”而且:GraphQL只需要一个网址URL!https://api.github.com/graphql不像REST,你需要各种各有的URL去申请不同的内容。GraphQL一个URL全够了。而且一般不是很复杂的情况下,你几乎只要request一次这个地址,就能拿到你全部需要的数据了(能按照你的需求返回给你各种嵌套的、格式化的数据)网上看了很多文章解释之后发现还是什么都没懂。所以这篇分享不打算按照常规路线,先用一大堆结构图、语法给你弄懵。这里我想先让它运作起来,有个"Hello world",然后再去深究背后的逻辑和语法。初试GraphQL要说去了解一个API,最好的方式就是用Postman或Insomnia这种REST客户端去连接玩耍了,不需要任何编程,只是手动点一点就ok。”有意思“的是,因为GraphQL太新潮,这两大客户端对它的支持各不相同,使用的参数、格式也大相径庭。下面通过最简单的案例总结一下。Insomnia 访问Github GraphQL 的案例Insomnia对GraphQL的支持相当好,可以说已经领先别人一步。以下为操作步骤:新建request, 设置为POST方式访问https://api.github.com/graphql申请授权认证:Github的API v4不能陌生访问,必须使用自己的账号申请一个token密码串,然后在每次连接API时使用。操作很简单,登录Github后,找到Settings -> Developer settings -> Personal access tokens -> Generate new token,生成一个新的token授权密码串,复制保存到本地备份。添加授权认证:在Insomnia软件里,有两种授权方式都可以达到同样认证效果:明文的Query参数里设置(即在url后面加上参数),或者Header表头里设置。Query里设置的话,格式为:?access_token=xxxxxxxxxxxHeader里设置的话,格式是:名称为Authorization,值为token xxxxxxxxxx。在栏目里面的Body位置选择GraphQL格式:输入Github指定格式的查询语句(看似JSON格式实则不是):query { viewer { login }}点击Send发送请求,如果一切正常,则会得到查询的返回值:Postman访问GraphQL失败不像Insomnia,Postman暂时没有支持GraphQL的选项,但是可以通过类似的操作达到一样的效果。流程是一样的,只是每个地方设置格式都不同,这也是我不断尝试才找到的总结方案(可惜网上相关教程太少)。这里只说不同的地方吧:授权比Insomnia多一种方式,可以在Authorization栏目里面直接选OAuth 2.0然后输入token密码串。最重要的是Body部分,查询语句完全不能使用Github指定的格式。只能选择Body格式为Raw -> JSON(application/json)。然后加上查询语句,格式如下(必须完全符合JSON语法):{ “query”: “query {viewer { login } }"}GitHub GraphQL Explorer这个是Github制作的网页练习机,免去了一切授权等处理,让你只专注于查询语句的调用练习。每次点击运行,都会实时返回对应的数据。如果记不住的、不懂的,还可以点击旁边的Docs,会显示出一个搜索框,显示你需要的内容的文档。GraphQL API Explorer | GitHub Developer Guide常用查询结构下面展示一些我测试过的查询结构,希望能起到帮助作用。为了增强阅读性,节省文字长度,返回值就先不粘上来了。而且返回值几乎就是查询语句的结构,没什么特别新鲜的。查询指定的repo中的issues和comments其中指定了某一个repo(通过owner和name指定),也指定了某一条issue(通过number指定),并要求返回最近的10条comments。

January 26, 2019 · 1 min · jiezi

翻译:github如何记录contributions

github contributions一般而言,任何一个人的github主页都有一个这样的方格图;每一个小格代表了one day,不同的颜色深度,代表了不同的contributions次数,那么这个次数是怎么计算的呢?本文的翻译就是上图底部的蓝色链接中的Learn how we count contributions.译文正文为什么我的contributions没有在个人profile中体现出来?个人profile中的contributions绘图是用于记录个人对github所做的contributions记录,贡献按照协调世界时(UTC)而不是您当地的时区加上时间戳。只有在满足特定标准的情况下才会当做contributions计算;在某些情况下,我们可能需要重建您的contributions绘图才能显示你的contributions。没有被记录的contributionsIssues和pull requests如果Issues和pull requests只有是在独立仓库中操作的,才会显示在您的contributions绘图上,而对于fork的仓库无法被记录的。Commits如果Commits符合以下所有条件,则会在您的contributions绘图上显示:用于Commits的电子邮件地址与您的GitHub帐户相关联Commits是在独立的仓库中进行的,而不是forkCommits在一下条件下完成在仓库的默认分支(通常是master)在gh-pages分支中(对于具有Project Pages站点的仓库)此外,必须至少满足下列条件之一:你是该仓库的协作者,或者是这个仓库所属组织的一员;你已经fork了该工程你对这个仓库提过Issues或者pull requests你star加星过这个仓库,通常的一些没有被记录的原因要显示在您的contributions绘图上,共同提交的提交必须符合与一个作者的提交相同的标准。当合并pull requests并且Commit时,只有合并pull requests的用户和打开pull requests的用户才会收到贡献积分。拉取请求的任何其他贡献者都不会获得贡献积分。当重新提交Commit时,Commit的原始作者和重新提交的人,无论是在命令行还是在GitHub上,都会收到贡献信用。Commit是在不到24小时前完成在完成符合要求的Commit后,您可能需要等待最多24小时才能看到贡献出现在您的贡献图表上。您尚未将本地Git提交电子邮件添加到您的个人资料中必须使用已添加到您的GitHub个人资料中的电子邮件地址进行提交,以便显示在您的贡献图表上。您可以通过将.patch添加到提交URL的末尾来检查用于提交的电子邮件地址,例如:https://github.com/octocat/octocat.github.io/commit/67c0afc1da354d8571f51b6f0af8f2794117fd10.patchFrom 67c0afc1da354d8571f51b6f0af8f2794117fd10 Mon Sep 17 00:00:00 2001From: The Octocat <octocat@nowhere.com>Date: Sun, 27 Apr 2014 15:36:39 +0530Subject: [PATCH] updated index for better welcome message可以配合:查看email:git config –global user.email 设置email:git config –global user.email XXXX@gmail.com未在默认master或gh-pages分支中进行提交只有在默认分支(通常为master)或gh-pages分支(对于具有Project Pages站点的存储库)中进行提交时才会计算提交。提交共同作者无权访问存储库如果在共同作者无权访问的存储库中进行了提交,则该提交将不计入共同作者的贡献。提交是在一个fork分支中进行的用fork做的提交不会计入你的贡献。要使它们计数,您必须执行以下操作之一:打开pull requests以将更改合并到父存储库中。要分离fork并将其转换为GitHub上的独立存储库,请联系GitHub支持或GitHub Premium支持。如果fork具有自己的分支,请让支持知道分支是否应随存储库移动到新网络中或保留在当前网络中。Commit是在合并和压缩的pull requests中完成的合并和压缩的pull requests中的Commit将不计入您的贡献。只有合并pull requests的用户和打开pull requests的用户才会收到贡献积分。拉取请求的任何其他贡献者都不会获得贡献积分。

January 26, 2019 · 1 min · jiezi

Spring Boot 实战 | 如何使用 IDEA 构建 Spring Boot 工程

微信公众号:一个优秀的废人如有问题或建议,请后台留言,我会尽力解决你的问题。前言新年立了个 flag,好好运营这个公众号。具体来说,就是每周要写两篇文章在这个号发表。刚立的 flag 可不能这么快打脸。下面送上本周第一篇。本文我们将介绍嵌入 Intellij IDEA 中的 Spring Initializr 工具,它同Web提供的创建功能一样,可以帮助我们快速的构建出一个基础的Spring Boot工程。什么是 SpringBoot ?SpringBoot 官方有一句话可以概括这个问题。那就是「约定大于配置」。这句话什么意思?相信学过 Spring 的人都知道,我们要手动写一大堆的 xml 文件用于配置,集成项目,才能使这个项目具备 web 的功能。而 SpringBoot 做了那些没有它你也会去做的Spring Bean配置。它使用「约定大于配置」的理念让你的项目快速运行起来。使用 Spring Boot 很容易创建一个独立运行(运行jar,内嵌Servlet容器)、准生产级别的基于 Spring 框架的项目,使用 Spring Boot 你可以不用或者只需要很少的Spring配置。如果说 Spring 是一辆汽车的引擎,那 SpringMVC 就给这辆汽车装上了轮子,而 SpringBoot 的出现就相当于赋予了这辆汽车自动驾驶的功能。如何使用 IDEA 构建 SpringBoot 工程?第一步,当然是安装 Intellij IDEA (傻瓜式教程,请自行百度)。点击菜单栏 File ➤New➤Project ➤ 选择 Spring Initializr 创建界面如下图,可以看到图中 default 指定的 Initializr Service URL 就是 Spring 官方提供的 Spring Initializr 工具地址,一般默认即可,所以这里创建的工程实际上也是基于它的 Web 工具来实现的。点击 next 进入下一步,可以看见这里要我们选择的就是关于工程的一些信息:Group 顾名思义就是你的公司名,一般是填写com.公司名。Artifact groupId 和 artifactId 是maven管理项目包时用作区分的字段,就像是地图上的坐标。这里填写项目名即可。Type 就是构建的项目类型,意思就是你希望你的项目使用什么工具构建,可选 maven 和 gradle 一般选 maven。Language 顾名思义就是你的项目用啥语言开发,可选 Java、Groovy、KotlinPackaging 就是你希望你的项目打成什么形式的包,可选 Jar、War SpringBoot 项目一般选 JarJava Version 意指项目使用的 java 版本,根据你的需要选择。Version 项目的初始版本,默认即可。Name 项目名称。Description 项目描述,默认即可。Package 包名,填完 Group 和 Artifact 后自动生成,默认即可。点击 Next 进入下一步,这一步就是选你的项目依赖包,前文所说的「约定大于配置」就体现在这里。进入选择S pring Boot 版本和依赖管理的窗口。在这里值的我们关注的是,它不仅包含了 Spring Boot Starter POMs 中的各个依赖,还包含了 Spring Cloud 的各种依赖。比如,你需要集成前端模板功能,你就到 Template Engines 选项卡上,勾选你想要访问的前端模板引擎 ,项目需要访问数据库,就到 SQL 选项卡,旋转你项目里使用的数据库类型。选择完成并加以简单的配置,项目就具备了集成前端模板能力与数据库访问能力。这里注意一下,无论你选择哪些依赖包,其中 web 选项卡下的 Web 是必选的。这个包是整个项目的基础。这个包里面集成了 Spring 、WebMvc 、tomcat 以及其他各种基本能力。点击 Next 进入下一步,这一步没啥好说的。就是让你确认自己的项目名以及项目路径。确认无误,点 Finish 完成创建即可。Intellij IDEA 中的 Spring Initializr 是基于官方 Web 实现,但是通过工具来进行调用并直接将结果构建到我们的本地文件系统中,让整个构建流程变得更加顺畅。后语我为什么要写这种这么简单的教程?是这样的,我始终认为比我聪明的人有很多,但比我笨的人也不少。在中国有很多你认为众所周知的事,其实有一车人根本不知道,这篇文章哪怕只帮助到一个人,足矣。之后我打算出一个 SpringBoot 系列的教程,敬请关注与指正,本人也是一个小菜鸟在打怪升级中,如本文有不正确的地方,烦请指正。一起学习一起进步。以上就是使用 IDEA 创建 SpringBoot 的过程,希望对你们有帮助。最后,对 Python 、Java 感兴趣请长按二维码关注一波,我会努力带给你们价值,如果觉得本文对你哪怕有一丁点帮助,请帮忙点好看,让更多人知道。 ...

January 25, 2019 · 1 min · jiezi

如何使用Git提高研发团队工作效率?

为什么使用Git随着互联网时代的来临与发展,尤其分布式开发的大力引入,对于开发工程师来说,代码管理变成了头等难题。10多个人或者更多的成员的研发团队如何管理同一份代码,异地办公如何跟同事有效的维护同一份代码?下面直接介绍Git,就不对Git和其他的版本管理工具进行比较了。Git属于分布式的版本控制系统,它具有以下特点:Git中每个克隆(clone)的版本库都是平等的。你可以从任何一个版本库的克隆来创建属于你自己的版本库,同时你的版本库也可以作为源提供给他人,只要你愿意。Git的每一次提取操作,实际上都是一次对代码仓库的完整备份。提交完全在本地完成,无须别人给你授权,你的版本库你作主,并且提交总是会成功。甚至基于旧版本的改动也可以成功提交,提交会基于旧的版本创建一个新的分支。Git的提交不会被打断,直到你的工作完全满意了,PUSH给他人或者他人PULL你的版本库,合并会发生在PULL和PUSH过程中,不能自动解决的冲突会提示您手工完成。冲突解决:在需要的时候才进行合并和冲突解决。Git版本库统一放在服务器中可以为 Git 版本库进行授权:谁能创建版本库,谁能向版本库PUSH,谁能够读取(克隆)版本库,即权限配置团队的成员先将服务器的版本库克隆到本地;并经常的从服务器的版本库拉(PULL)最新的更新;团队的成员将自己的改动推(PUSH)到服务器的版本库中,当其他人和版本库同步(PULL)时,会自动获取改变你完全可以在脱离Git服务器所在网络的情况下,如移动办公/出差时,照常使用代码库你只需要在能够接入Git服务器所在网络时,PULL和PUSH即可完成和服务器同步以及提交选择适合团队的工作流分布式工作流程:同传统的集中式版本控制系统(CVCS)不同,Git 的分布式特性使得开发者间的协作变得更加灵活多样。在集中式系统中,每个开发者就像是连接在集线器上的节点,彼此的工作方式大体相像。而在 Git 中,每个开发者同时扮演着节点和集线器的角色——也就是说,每个开发者既可以将自己的代码贡献到其他的仓库中,同时也能维护自己的公开仓库,让其他人可以在其基础上工作并贡献代码。由此,Git 的分布式协作可以为你的项目和团队衍生出种种不同的工作流程,接下来的章节会介绍几种利用了 Git 的这种灵活性的常见应用方式。我们将讨论每种方式的优点以及可能的缺点;你可以选择使用其中的某一种,或者将它们的特性混合搭配使用。Git 提供的有以下三种工作流程集中式工作流集成管理者工作流司令官与副官工作流目前我们团队使用的是最简单的方式,集中式工作流程,随着研发团队的壮大可能会选择使用第二种,下面我们分别介绍下三种工作流1.集中式工作流 集中式系统中通常使用的是单点协作模型——集中式工作流。一个中心集线器,或者说仓库,可以接受代码,所有人将自己的工作与之同步。若干个开发者则作为节点——也就是中心仓库的消费者——并且与其进行同步。这意味着如果两个开发者从中心仓库克隆代码下来,同时作了一些修改,那么只有第一个开发者可以顺利地把数据推送回共享服务器。第二个开发者在推送修改之前,必须先将第一个人的工作合并进来,这样才不会覆盖第一个人的修改。这和 Subversion (或任何 CVCS)中的概念一样,而且这个模式也可以很好地运用到 Git 中。如果在公司或者团队中,你已经习惯了使用这种集中式工作流程,完全可以继续采用这种简单的模式。只需要搭建好一个中心仓库,并给开发团队中的每个人推送数据的权限,就可以开展工作了。Git 不会让用户覆盖彼此的修改。例如 John 和 Jessica 同时开始工作。 John完成了他的修改并推送到服务器。接着 Jessica 尝试提交她自己的修改,却遭到服务器拒绝。她被告知她的修改正通过非快进式(non-fast-forward)的方式推送,只有将数据抓取下来并且合并后方能推送。这种模式的工作流程的使用非常广泛,因为大多数人对其很熟悉也很习惯。当然这并不局限于小团队。利用 Git 的分支模型,通过同时在多个分支上工作的方式,即使是上百人的开发团队也可以很好地在单个项目上协作。2.集成管理者工作流 Git 允许多个远程仓库存在,使得这样一种工作流成为可能:每个开发者拥有自己仓库的写权限和其他所有人仓库的读权限。这种情形下通常会有个代表官方''项目的权威的仓库。要为这个项目做贡献,你需要从该项目克隆出一个自己的公开仓库,然后将自己的修改推送上去。接着你可以请求官方仓库的维护者拉取更新合并到主项目。维护者可以将你的仓库作为远程仓库添加进来,在本地测试你的变更,将其合并入他们的分支并推送回官方仓库。这一流程的工作方式如下所示:项目维护者推送到主仓库。贡献者克隆此仓库,做出修改。贡献者将数据推送到自己的公开仓库。贡献者给维护者发送邮件,请求拉取自己的更新。维护者在自己本地的仓库中,将贡献者的仓库加为远程仓库并合并修改。维护者将合并后的修改推送到主仓库。这是 GitHub 和 GitLab 等集线器式(hub-based)工具最常用的工作流程。人们可以容易地将某个项目派生成为自己的公开仓库,向这个仓库推送自己的修改,并为每个人所见。这么做最主要的优点之一是你可以持续地工作,而主仓库的维护者可以随时拉取你的修改。贡献者不必等待维护者处理完提交的更新——每一方都可以按照自己节奏工作。3.司令官与副官工作流 这其实是多仓库工作流程的变种。一般拥有数百位协作开发者的超大型项目才会用到这样的工作方式,例如著名的 Linux 内核项目。被称为副官(lieutenant)的各个集成管理者分别负责集成项目中的特定部分。所有这些副官头上还有一位称为司令官(dictator)的总集成管理者负责统筹。司令官维护的仓库作为参考仓库,为所有协作者提供他们需要拉取的项目代码。整个流程看起来是这样的普通开发者在自己的特性分支上工作,并根据master分支进行变基。这里是司令官的master分支。副官将普通开发者的特性分支合并到自己的master分支中。司令官将所有副官的master分支并入自己的master分支中。司令官将集成后的master分支推送到参考仓库中,以便所有其他开发者以此为基础进行变基。这种工作流程并不常用,只有当项目极为庞杂,或者需要多级别管理时,才会体现出优势。利用这种方式,项目总负责人(即司令官)可以把大量分散的集成工作委托给不同的小组负责人分别处理,然后在不同时刻将大块的代码子集统筹起来,用于之后的整合。介绍了上面三种工作流,想必你一定有想法使用哪一种了,选择一个适合自己团队的工作流,只要是严格按照这种规则执行的话,相信你的研发团队对于代码的管理一定不会再混乱了,这也会大大提升团队的工作效率。管理 Git 分支几乎所有的版本控制系统都以某种形式支持分支。使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线。在很多版本控制系统中,这是一个略微低效的过程——常常需要完全创建一个源代码目录的副本。对于大项目来说,这样的过程会耗费很多时间。有人把 Git 的分支模型称为它的必杀技特性’’,也正因为这一特性,使得 Git 从众多版本控制系统中脱颖而出。为何 Git 的分支模型如此出众呢? Git 处理分支的方式可谓是难以置信的轻量,创建新分支这一操作几乎能在瞬间完成,并且在不同分支之间的切换操作也是一样便捷。与许多其它版本控制系统不同,Git 鼓励在工作流程中频繁地使用分支与合并,哪怕一天之内进行许多次。理解和精通这一特性,你便会意识到 Git 是如此的强大而又独特,并且从此真正改变你的开发方式。Git 创建分支 Git 是怎么创建新分支的呢?很简单,它只是为你创建了一个可以移动的新的指针。比如,创建一个 testing 分支,你需要使用git branch命令:$ git branch testing这会在当前所在的提交对象上创建一个指针那么,Git 又是怎么知道当前在哪一个分支上呢?也很简单,它有一个名为HEAD的特殊指针。请注意它和许多其它版本控制系统(如 Subversion 或 CVS)里的HEAD概念完全不同。在 Git 中,它是一个指针,指向当前所在的本地分支。在本例中,你仍然在master分支上。因为git branch命令仅仅创建一个新分支,并不会自动切换到新分支中去。你可以简单地使用git log命令查看各个分支当前所指的对象。提供这一功能的参数是–decorate。$ git log –oneline –decoratef30ab (HEAD, master, testing) add feature #32 - ability to add new34ac2 fixed bug #1328 - stack overflow under certain conditions98ca9 initial commit of my project正上,当前master 和testing 分支均指向校验和以f30ab开头的提交对象。分支切换 要切换到一个已存在的分支,你需要使用git checkout命令。我们现在切换到新创建的testing分支去:$ git checkout testing这样HEAD就指向testing分支了。那么,这样的实现方式会给我们带来什么好处呢?现在不妨再提交一次:$ vim test.rb$ git commit -a -m ‘made a change’如上图所示,你的testing分支向前移动了,但是master分支却没有,它仍然指向运行git checkout时所指的对象。这就有意思了,现在我们切换回master分支看看:$ git checkout master这条命令做了两件事。一是使 HEAD 指回master分支,二是将工作目录恢复成master分支所指向的快照内容。也就是说,你现在做修改的话,项目将始于一个较旧的版本。本质上来讲,这就是忽略testing分支所做的修改,以便于向另一个方向进行开发。现在我们稍微再做些修改并commit:$ vim test.rb$ git commit -a -m ‘made other changes’现在,这个项目的提交历史已经产生了分叉。因为刚才你创建了一个新分支,并切换过去进行了一些工作,随后又切换回 master 分支进行了另外一些工作。上述两次改动针对的是不同分支:你可以在不同分支间不断地来回切换和工作,并在时机成熟时将它们合并起来。而所有这些工作,你需要的命令只有branch、checkout和commit。 Git 合并分支接下来咱们举个稍微复杂点的例子,三个分支的分别处理不同的事情,最后合并到一块首先,我们假设你正在你的项目上工作,并且已经有一些提交, 如下图:现在,你已经决定要解决你的公司使用的问题追踪系统中的 #53 问题。想要新建一个分支并同时切换到那个分支上,你可以运行一个带有-b参数的git checkout命令:$ git checkout -b iss53Switched to a new branch “iss53"它是下面两条命令的简写:$ git branch iss53$ git checkout iss53你继续在 #53 问题上工作,并且做了一些提交。在此过程中,iss53分支在不断的向前推进,因为你已经检出到该分支(也就是说,你的HEAD指针指向了iss53分支)$ vim index.html$ git commit -a -m ‘added a new footer [issue 53]‘当你在iss53这个分支上正在顺畅的工作,这时候有个特别紧急的问题需要你来修复,那么为了不影响iss53的正常工作,你需要做的是重新切换到master分支上来,在master分支的基础上再创建一个新的分支 hotfix ,然后在hotfix分支解决紧急的问题。$ git checkout -b hotfixSwitched to a new branch ‘hotfix’$ vim index.html$ git commit -a -m ‘fixed the broken email address’[hotfix 1fb7853] fixed the broken email address 1 file changed, 2 insertions(+)这个时候hotfix分支上的问题彻底解决了,你需要合并到master分支,并且部署上线, 那么你只需要在master分支使用git merge命令就可以了$ git checkout master$ git merge hotfixUpdating f42c576..3a0874cFast-forward index.html | 2 ++ 1 file changed, 2 insertions(+)在合并的时候,你应该注意到了"快进(fast-forward)“这个词。由于当前master分支所指向的提交是你当前提交(有关 hotfix 的提交)的直接上游,所以 Git 只是简单的将指针向前移动。换句话说,当你试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候,只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧——这就叫做 “快进(fast-forward)”。现在,最新的修改已经在master分支所指向的提交快照中了。关于这个紧急问题的解决方案发布之后,你准备回到被打断之前时的工作中。然而,你应该先删除hotfix分支,因为你已经不再需要它了, master分支已经指向了同一个位置。你可以使用带-d选项的git branch命令来删除分支:$ git branch -d hotfixDeleted branch hotfix (3a0874c).现在你可以切换回你正在工作的分支继续你的工作,也就是针对iss53分支$ git checkout iss53Switched to branch “iss53”$ vim index.html$ git commit -a -m ‘finished the new footer [issue 53]’[iss53 ad82d7a] finished the new footer [issue 53]1 file changed, 1 insertion(+)这个时候 #53 问题解决后你就可以把代码合并到master了,操作跟刚才的htofix分支处理方式一样$ git checkout masterSwitched to branch ‘master’$ git merge iss53Merge made by the ‘recursive’ strategy.index.html | 1 +1 file changed, 1 insertion(+)但是这和你之前合并hotfix分支的时候看起来有一点不一样。在这种情况下,你的开发历史从一个更早的地方开始分叉开来(diverged)。因为,master分支所在提交并不是iss53分支所在提交的直接祖先,Git 不得不做一些额外的工作。出现这种情况的时候,Git 会使用两个分支的末端所指的快照(C4和C5)以及这两个分支的工作祖先(C2),做一个简单的三方合并。和之间将分支指针向前推进所不同的是,Git 将此次三方合并的结果做了一个新的快照并且自动创建一个新的提交指向它。这个被称作一次合并提交,它的特别之处在于他有不止一个父提交。这个时候如何不需要iss53分支的话,你也可以删除iss53分支了 远程分支以github为例,目前很多开源项目以及公司的研发项目代码一般都托管在github,那么就出现了远程仓库,远程分支等这些概念。下面我们从github远程仓库clone下来一份代码如果你在本地的master分支做了一些工作,然而在同一时间,其他人推送提交到git@github.com:glj1102/git_test.git 并更新了它的master分支,那么你的提交历史将向不同的方向前进。也许,只要你不与 origin 服务器连接,你的origin/master指针就不会移动。如果要同步你的工作,运行git pull命令。这个命令是抓取远程分支数据到本地分支,并且更新本地数据库,移动origin/master指针指向新的、更新后的位置。同时你也可以执行 git push命令把你本地修改的数据提交到远程分支。实时跟踪和监控团队代码操作记录我们使用的是 Worktile + github 来管理团队的,平时团队沟通,任务分配都是通过Worktile来做的,那么 Worktile 与github如何关联的呢? Worktile绑定github代码仓储在Worktile的后台管理 > 服务管理中找到github,点击添加下一步会让你选择Worktile的一个聊天群组,之后github的提交记录就会发送到这个群组中:添加服务之后需要进行一些配置,配置方式有两种,一种是授权模式,选择仓储,选择事件, 另一种是普通模式,这种模式Worktile会生成一个Webhook链接,拿到这个链接可以直接在github仓储中进行Webhook配置:具体的配置Worktile后台github服务设置中有教程。 Worktile接收github发送的操作记录根据不配置时选择的事件不同,github会发送不同的记录消息总结通过上面对Git的介绍,对于团队管理者来说,如何使用Git来有效的管理团队代码应该会有最基本的概念,那接下来就需要你真正的花时间来实践了。那么对于Git的命令集网上有很多例子的,下面我这里介绍两本Git的基本入门教程:Git 简易指南 http://www.bootcss.com/p/git-...Git 图解 http://marklodato.github.io/v… ...

January 24, 2019 · 2 min · jiezi

Git合并不同url的项目

本文由云+社区发表作者:工程师小熊摘要:为了让项目能实现Git+Gerrit+Jenkin的持续集成,我们把项目从Git上迁移到了Gerrit上,发现有的同事在老Git提交代码,因为Gerrit做了同步,在Gerrit上有新提交的时候就会刷新老git,这样就会把他提交的代码冲掉。这个时候我就必须要在两个相似项目之间合并提交了。步骤将老Git url加到我们新Git的本地使用命令git remote add [shortname] [url]将老Git url加到我们新Git的本地这里我把他取名为gitoa_web(随便取)查看使用命令git remot -v查看远程仓库的情况可以看到此处我们有三个远程仓库分别名为gerrit、 gitoa_web、origin同步代码使用命令git fetch gitoa_web刷新远程仓库到本地字符串 gitoa_web 指代对应的仓库地址了.比如说,要抓取所有 gitoa_web 有的,但本地仓库没有的信息,可以用合并项目使用命令git merge gitoa_web/master合并项目gitoa_web是指代仓库,master指代分支,当然如果有需要也可以合并别的分支过来 报错发现不同email地址错误不能成功提交因为这个commit不是我的修正错误把email地址更新成我的再提交就成功了小结知识点:git merge还可以合并其他项目的到本项目git fetch 仓库名可以指定同步哪个仓库git remot -v查看本地有哪些远程仓库的情况,包含各个仓库url本次我们对以下命令加深了理解git remote #不带参数,列出已经存在的远程分支git remote -v #(-v是–verbose 的简写,取首字母)列出详细信息,在每一个名字后面列出其远程urlgit remote add [shortname] [url] #添加远程仓库git fetch origin #字符串 origin 指代对应的仓库地址了.比如说,要抓取所有 origin 有的,但本地仓库没有的信息,可以用ps: 这里git remote add以后,我认为还能用cherry-pick来加不同仓库的commit过来,有兴趣的朋友可以自己尝试。附Git常用命令此文已由腾讯云+社区在各渠道发布获取更多新鲜技术干货,可以关注我们腾讯云技术社区-云加社区官方号及知乎机构号

January 23, 2019 · 1 min · jiezi

长连接的心跳及重连设计

前言说道“心跳”这个词大家都不陌生,当然不是指男女之间的心跳,而是和长连接相关的。顾名思义就是证明是否还活着的依据。什么场景下需要心跳呢?目前我们接触到的大多是一些基于长连接的应用需要心跳来“保活”。由于在长连接的场景下,客户端和服务端并不是一直处于通信状态,如果双方长期没有沟通则双方都不清楚对方目前的状态;所以需要发送一段很小的报文告诉对方“我还活着”。同时还有另外几个目的:服务端检测到某个客户端迟迟没有心跳过来可以主动关闭通道,让它下线。客户端检测到某个服务端迟迟没有响应心跳也能重连获取一个新的连接。正好借着在 cim有这样两个需求来聊一聊。心跳实现方式心跳其实有两种实现方式:TCP 协议实现(keepalive 机制)。应用层自己实现。由于 TCP 协议过于底层,对于开发者来说维护性、灵活度都比较差同时还依赖于操作系统。所以我们这里所讨论的都是应用层的实现。如上图所示,在应用层通常是由客户端发送一个心跳包 ping 到服务端,服务端收到后响应一个 pong 表明双方都活得好好的。一旦其中一端延迟 N 个时间窗口没有收到消息则进行不同的处理。客户端自动重连先拿客户端来说吧,每隔一段时间客户端向服务端发送一个心跳包,同时收到服务端的响应。常规的实现应当是:开启一个定时任务,定期发送心跳包。收到服务端响应后更新本地时间。再有一个定时任务定期检测这个“本地时间”是否超过阈值。超过后则认为服务端出现故障,需要重连。这样确实也能实现心跳,但并不友好。在正常的客户端和服务端通信的情况下,定时任务依然会发送心跳包;这样就显得没有意义,有些多余。所以理想的情况应当是客户端收到的写消息空闲时才发送这个心跳包去确认服务端是否健在。好消息是 Netty 已经为我们考虑到了这点,自带了一个开箱即用的 IdleStateHandler 专门用于心跳处理。来看看 cim 中的实现:在 pipeline 中加入了一个 10秒没有收到写消息的 IdleStateHandler,到时他会回调 ChannelInboundHandler 中的 userEventTriggered 方法。所以一旦写超时就立马向服务端发送一个心跳(做的更完善应当在心跳发送失败后有一定的重试次数);这样也就只有在空闲时候才会发送心跳包。但一旦间隔许久没有收到服务端响应进行重连的逻辑应当写在哪里呢?先来看这个示例:当收到服务端响应的 pong 消息时,就在当前 Channel 上记录一个时间,也就是说后续可以在定时任务中取出这个时间和当前时间的差额来判断是否超过阈值。超过则重连。同时在每次心跳时候都用当前时间和之前服务端响应绑定到 Channel 上的时间相减判断是否需要重连即可。也就是 heartBeatHandler.process(ctx); 的执行逻辑。伪代码如下:@Overridepublic void process(ChannelHandlerContext ctx) throws Exception { long heartBeatTime = appConfiguration.getHeartBeatTime() * 1000; Long lastReadTime = NettyAttrUtil.getReaderTime(ctx.channel()); long now = System.currentTimeMillis(); if (lastReadTime != null && now - lastReadTime > heartBeatTime){ reconnect(); }}IdleStateHandler 误区一切看起来也没毛病,但实际上却没有这样实现重连逻辑。最主要的问题还是对 IdleStateHandler 理解有误。我们假设下面的场景:客户端通过登录连上了服务端并保持长连接,一切正常的情况下双方各发心跳包保持连接。这时服务端突入出现 down 机,那么理想情况下应当是客户端迟迟没有收到服务端的响应从而 userEventTriggered 执行定时任务。判断当前时间 - UpdateWriteTime > 阈值 时进行重连。但却事与愿违,并不会执行 2、3两步。因为一旦服务端 down 机、或者是与客户端的网络断开则会回调客户端的 channelInactive 事件。IdleStateHandler 作为一个 ChannelInbound 也重写了 channelInactive() 方法。这里的 destroy() 方法会把之前开启的定时任务都给取消掉。所以就不会再有任何的定时任务执行了,也就不会有机会执行这个重连业务。靠谱实现因此我们得有一个单独的线程来判断是否需要重连,不依赖于 IdleStateHandler。于是 cim 在客户端感知到网络断开时就会开启一个定时任务:之所以不在客户端启动就开启,是为了节省一点线程消耗。网络问题虽然不可避免,但在需要的时候开启更能节省资源。在这个任务重其实就是执行了重连,限于篇幅具体代码就不贴了,感兴趣的可以自行查阅。同时来验证一下效果。启动两个服务端,再启动客户端连接上一台并保持长连接。这时突然手动关闭一台服务,客户端可以自动重连到可用的那台服务节点。启动客户端后服务端也能收到正常的 ping 消息。利用 :info 命令查看当前客户端的链接状态发现连的是 9000端口。:info 是一个新增命令,可以查看一些客户端信息。这时我关掉连接上的这台节点。kill -9 2142这时客户端会自动重连到可用的那台节点。这个节点也收到了上线日志以及心跳包。服务端自动剔除离线客户端现在来看看服务端,它要实现的效果就是延迟 N 秒没有收到客户端的 ping 包则认为客户端下线了,在 cim 的场景下就需要把他踢掉置于离线状态。消息发送误区这里依然有一个误区,在调用 ctx.writeAndFlush() 发送消息获取回调时。其中是 isSuccess 并不能作为消息发送成功与否的标准。也就是说即便是客户端直接断网,服务端这里发送消息后拿到的 success 依旧是 true。这是因为这里的 success 只是告知我们消息写入了 TCP 缓冲区成功了而已。和我之前有着一样错误理解的不在少数,这是 Netty 官方给的回复。相关 issue:https://github.com/netty/netty/issues/4915同时感谢 95老徐以及闪电侠的一起排查。所以我们不能依据此来关闭客户端的连接,而是要像上文一样判断 Channel 上绑定的时间与当前时间只差是否超过了阈值。以上则是 cim 服务端的实现,逻辑和开头说的一致,也和 Dubbo 的心跳机制有些类似。于是来做个试验:正常通信的客户端和服务端,当我把客户端直接断网时,服务端会自动剔除客户端。总结这样就实现了文初的两个要求。服务端检测到某个客户端迟迟没有心跳过来可以主动关闭通道,让它下线。客户端检测到某个服务端迟迟没有响应心跳也能重连获取一个新的连接。同时也踩了两个误区,坑一个人踩就可以了,希望看过本文的都有所收获避免踩坑。本文所有相关代码都在此处,感兴趣的可以自行查看:https://github.com/crossoverJie/cim如果本文对你有所帮助还请不吝转发。 ...

January 23, 2019 · 1 min · jiezi

啥是佩奇排名算法

佩奇排名介绍佩奇排名是根据页面之间的链接结构计算页面的值的一种算法。下面我们通过动画来理解进行计算的具体流程。假设一个正方形表示一个 WEB 页面,一个箭头表示一个页面之间的链接。在佩奇排名算法中,网页指向的链接越多,页面被确定为越重要。因此,在这里,确定首页最重要。实际上,每个页面的重要性都是通过计算来量化的。基本的计算方法思想1.未链接的页面分数为 12.有链接的页面得分为正在链接的页面的总得分3.当有多个网页的链接时,链接分数均匀分布4.来自高度链接网页的链接具有很高的价值该图中心页面有三个独立页面指向它的链接,所以它的分数是 3 。 首页有一个很大的分数,因为链接是从分数为 3 的页面指向它的。在动画中的六个页面中,判断最上面的页面是最重要的页面—-这是佩奇排名的基本思想。基本的计算方法思想的循环问题如果按照顺序来计算每个页面的分数时,那么就会出现问题:以这种方式计算,它将无限循环,并且在循环中的页面得分在任何地方都会很高。循环的问题可以通过“随机游走模型”的计算方法来解决。随机游走模型以小猪佩奇浏览网页为例。小猪佩奇开始访问「五分钟学算法」中有趣的页面,那么从这个左下角页面开始。它们跟随一个链接并移动到另外的一个页面,看了一些之后,发现不敢兴趣了,这样就停止了浏览。然后,又一天,它在小吴的推荐下,在完全不同的页面进行浏览,跟随一个链接并移动到另外的一个页面,一旦失去兴趣就停止浏览。像这样,重复从某个页面开始浏览,移动几页后便停止的操作,如果从互联网空间一侧进行观察,就像网页浏览的人:重复移动页面几次后传送到一个完全不同的页面。量化随机游走模型假设 1 - 代表选择当前页面中的一个链接的概率。代表该人将传送到其他页面的概率。现在用 随机游走模型 处理上述的循环问题。如果总页面访问次数达到1000次之后,使用百分比进行表示:那么这个值就表示“在某个时间点查看页面的概率”。### 更实用的计算方法如图所示,现在来尝试计算复杂的链接网络中每个页面的分数。现在均匀设置分数,使总分加起来为 1 。而后根据网页浏览者的移动,来计算每个页面的概率。移动 n 次时出现在 A 中的概率表示未 PAn,移动 n 次时出现在 B 中的概率表示未 PBn。举一个例子,在移动 1 次之后求在 A 的概率 PA 1。在 C 选择移动的概率是 1-。其中,移动到 A 的一种场景是,C 中的佩奇选择了移动而不是传送。另外,这里选择了 A 而不是 B 作为目的地。 并且,根据上面的 当有多个网页的链接时,链接分数均匀分布 这条规则,从 A 或 B 选择 A 的概率是 0.5 。因此,从 C 移动到 A 的概率是 PC0 ✖️ (1-) ✖️ 0.5。A 被选为传送目标的概率是 0.25 ,根据前面的理论:在 A、B、C、D 中小佩奇选择传送的概率为 。因此,通过传送移动到 A 的概率为 ✖️ 0.25。 所以,移动一次后在 A 的概率为 PA1 = PC0 ✖️ ( 1 - ) ✖️ 0.5 + ✖️ 0.25其中 PC0 = 0.25 , = 0.15,代入计算后 PA1 = 0.14375。这样,通过计算后 B 、 C 、D 页的概率也更新了。上面在移动 1 次之后这四个页面的概率更新情况,根据上述相同的方法计算 2 次后小佩奇浏览在每个页面的概率。同样的,经过大量的移动,在每个页面上的概率逐渐趋于固定值。当数值固定是,计算也就完成了。End佩奇排名就是这样一种通过访问概率代替链接的权重来计算的机制。 ...

January 22, 2019 · 1 min · jiezi

Git 问题,问有多少 tree,多少 blob(附答案)

注:下文的题目来自于极客时间的「玩转Git三剑客」。本文优先在公众号:善始者實繁克終者蓋寡 中发出。目录问题答案推荐一篇 GitHub 文章问题新建一个Git仓库,有且仅有1个commit,仅仅包含 /doc/readme ,请问内含多少个tree,多少个blob?如图:答案一个 commit,两个 tree,一个 blob。推荐一篇 GitHub 文章早上 8:31,团长在「公众号:千古壹号」中发布了一篇文章GitHub 是目前唯一的还有流量的红利的写作平台,推荐给大家看看。

January 21, 2019 · 1 min · jiezi

百度云智能边缘GitHub上好评首次超过微软,它做对了什么?

百度云的边缘计算能力正在国际上获得越来越大的关注度。近日,在拥有超过 3000 万名开发者的开源社区GitHub上,百度云智能边缘的好评(star数量)首次超过微软。 1月16日,百度云智能边缘在GitHub上获得star数量6231月16日,微软边缘计算在GitHub上获得star数量580GitHub 是迄今为止最流行、同类网站中最大的项目管理和开源协作平台,拥有9600 万个开源项目。托管的版本中不乏知名开源项目,如 Ruby on Rails、jQuery、python 等。很多知名公司和组织在上面托管了开源软件项目,包括苹果、亚马逊、Facebook、谷歌等。有机构预测,到2020年,全球将有500亿台设备同时连接网络。IDC预测,到2021年全球有43%的物联网计算将发生在边缘。这些庞大的终端数量对运算速度、网络带宽都提出了更高的要求。比如一辆自动驾驶汽车在运行过程中,车载计算系统需要即时分析、反馈各种传感器的信息,引导发动机、车轮、制动器等运行。如果通过传统的云计算来处理,高响应延迟就会对交通安全造成重大影响。由于更靠近数据生成的设备端,边缘计算有着诸多“先天优势”。它能更实时、更快速地处理数据,对网络带宽需求也较低。此外还对让数据隐私具有较高的保护作用。全球两大知名IT市场研究公司Gartner与IDC发布的2019年预测中,都将边缘计算列为了“战略性技术趋势”和“热点产业”。 云端共存、云端互动将是未来云计算市场的发展趋势。目前,亚马逊、微软、谷歌等科技巨头都在积极探索“边缘计算”技术,亚马逊AWS 的Greengrass、谷歌的Edge TPU和Cloud IoT Edge相继推出,微软发布了Azure IoT Edge服务和开源Azure IoT Edge Runime。在中国科技公司中,百度云在边缘计算的布局处于领先地位。2018年5月,百度云发布了中国首个智能边缘产品BIE (Baidu Intelligent Edge)。同年12月,百度云推出国内首个开源边缘计算平台OpenEdge,宣布全面对外开源。刚刚结束的拉斯维加斯CES现场,百度云分别联合英特尔和恩智浦(NXP)重磅发布两款边缘计算硬件设备,全场瞩目。这也是百度智能云事业群组在去年12月战略升级后,携全方位领先技术及产品的首次集体亮相。百度云还率先将智能边缘技术快速落地应用在农业、城市环卫等诸多领域。在中国湖北、江苏、辽宁和安徽四个省份,麦飞科技借助百度云边缘计算架构,实现了在无人机上直接运行识别算法,避免了数据传送到云端、识别结果发送到设备的延时。通过这种“精准用药”,辽宁盘锦一位农户的农药使用量降低50%;在江苏苏州,跑在路上的渣土车搭载了BIE-AI-BOX,就能实时识别渣土掉落情况,及时发现、提醒司机处理,还能把相关信息报告给环卫部门。百度云智能边缘在GitHub上好评首次超过微软,不仅是百度云技术已处于全球领先,也是全球开发者们对中国科技企业实力的认可。当下,中国经济正处于转型中。边缘计算将有助于AI在各个行业中的快速落地,加速万物互联时代的来临,这也将加速中国经济智能化升级步伐。

January 21, 2019 · 1 min · jiezi

gitbook使用教程

说明gitbook网站是一个简单的个人在线书籍网站,在这里可以把自己的文档整理成书籍发布出来,便于阅读。gitbook网站:https://legacy.gitbook.com/本文主要讲解在gitbook网站上发布一个书籍文档和使用gitbook提供的工具在本地开发一个书籍文档部署到自己的服务上在此之前你需要会如下准备:账号: github有账号,gitbook使用github账号注册git:代码管理工具Markdown:gitbook主要使用MD语法来编写书籍的gitbook工具:如果你在本地开发需要安装此插件,下面有介绍nodejs环境:gitbook插件需要的运行环境一款Markdown编辑器:方便本地开发,推荐Typora或gitbook自己的编辑器gitbook editor2. 在gitbook网站上创建一本文档书籍此种方式是使用github网站加gitbook网站的方式来创建书籍的。首先你要有一个github账号,然后在github网站中创建了一个repo仓库,用于存放书籍内容的仓库。注意:gitbook网站有时需要使用代理才能打开。2.1 登陆gitbook网站gitbook网站支持直接使用github账号登陆的,推荐直接使用github账号登陆。打开gitbook网站:https://legacy.gitbook.com/点击右上角的Sign In登陆,然后选择Sign in with GitHub选择使用github账号进行登陆。第一次登陆gitbook网站时,需要github网站的认证,还需要到注册github网站的邮箱中点击确认。2.2 创建一本书2.2.1 点击新建一本书按钮登陆网站后,点击右上角的用户图标,然后选择Your Profile。然后就会在右上角看见+ new按钮,点击此按钮就是创建一本书。或者在gitbook的dashboard页面有一个New Book按钮,点击也可以创建一个书籍:或者打开此链接,直接进入创建书籍页面:https://legacy.gitbook.com/new2.2.2 在创建书籍中选择github

January 21, 2019 · 1 min · jiezi

我的一些开源项目(前端)

前言之前陆陆续续在 GitHub 上创建了几个项目,奈何没人关注(可能我的项目太垃圾了)。于是,我决定厚着脸皮来宣传一下我的几个开源项目,虽然大多数都是一些比较简单的游戏,但是这可以让更多人看到我的项目,也可以让我自己知道哪里地方做得不好,并且加以改进。正文1. 清技背单词SPA使用 Vue + Element UI + Lumen 开发的背单词 SPA 应用,开发时间为一个月,目前是1.0版本。部分截图:项目地址:链接2. Star Battle(星际大战)使用原生JavaScript ES6 + Canvas 开发的一个类似雷电战机的 HTML5 游戏,开发时间为一个星期。游戏截图:PC端可在线玩:链接项目地址:链接3. 2048使用原生 JavaScript 开发的一个2048 HTML5 游戏,开发时间为3天。游戏截图:移动端和PC端都可在线玩:链接项目地址:链接4. 连连看使用原生 JavaScript 开发的一个连连看 HTML5 游戏,开发时间为3天。游戏截图:移动端和PC端都可在线玩:链接项目地址:链接5. 便签应用React 开发的一个便签应用,开发时间为3天。截图:预览地址:链接项目地址:链接6. 运算大师React 开发的一个运算大师游戏(仿微信小游戏),开发时间为3天。在线玩:链接项目地址:链接7. 纯CSS轮播闲的无聊做的,使用CSS实现轮播功能(可点击)。在线地址:链接项目地址:链接8. 更早之前的小项目这是我刚开始学习时做的一些小Demos,有游戏、动画效果,也有网站。导航:https://gd4ark.github.io/web_…项目地址:https://github.com/gd4Ark/web…9. 个人博客使用 Hexo + GitHub Pages 搭建的个人博客。在线地址:链接项目地址:链接注:此文为原创文章,如需转载,请注明出处。

January 20, 2019 · 1 min · jiezi

java 日志脱敏框架 sensitive-v0.0.4 系统内置常见注解,支持自定义注解

项目介绍日志脱敏是常见的安全需求。普通的基于工具类方法的方式,对代码的入侵性太强。编写起来又特别麻烦。本项目提供基于注解的方式,并且内置了常见的脱敏方式,便于开发。特性基于注解的日志脱敏。可以自定义策略实现,策略生效条件。常见的脱敏内置方案。java 深拷贝,且原始对象不用实现任何接口。支持用户自定义注解。自定义注解maven 导入<dependency> <groupId>com.github.houbb</groupId> <artifactId>sensitive</artifactId> <version>0.0.4</version></dependency>自定义注解v0.0.4 新增功能。允许功能自定义条件注解和策略注解。案例自定义注解策略脱敏/** * 自定义密码脱敏策略 * @author binbin.hou * date 2019/1/17 * @since 0.0.4 /@Inherited@Documented@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@SensitiveStrategy(CustomPasswordStrategy.class)public @interface SensitiveCustomPasswordStrategy {}脱敏生效条件/* * 自定义密码脱敏策略生效条件 * @author binbin.hou * date 2019/1/17 * @since 0.0.4 /@Inherited@Documented@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@SensitiveCondition(ConditionFooPassword.class)public @interface SensitiveCustomPasswordCondition{}TIPS@SensitiveStrategy 策略单独使用的时候,默认是生效的。如果有 @SensitiveCondition 注解,则只有当条件满足时,才会执行脱敏策略。@SensitiveCondition 只会对系统内置注解和自定义注解生效,因为 @Sensitive 有属于自己的策略生效条件。策略优先级@Sensitive 优先生效,然后是系统内置注解,最后是用户自定义注解。对应的实现两个元注解 @SensitiveStrategy、@SensitiveCondition 分别指定了对应的实现。CustomPasswordStrategy.javapublic class CustomPasswordStrategy implements IStrategy { @Override public Object des(Object original, IContext context) { return “”; }}ConditionFooPassword.java/** * 让这些 123456 的密码不进行脱敏 * @author binbin.hou * date 2019/1/2 * @since 0.0.1 /public class ConditionFooPassword implements ICondition { @Override public boolean valid(IContext context) { try { Field field = context.getCurrentField(); final Object currentObj = context.getCurrentObject(); final String name = (String) field.get(currentObj); return !name.equals(“123456”); } catch (IllegalAccessException e) { throw new RuntimeException(e); } }}定义测试对象定义一个使用自定义注解的对象。public class CustomPasswordModel { @SensitiveCustomPasswordCondition @SensitiveCustomPasswordStrategy private String password; @SensitiveCustomPasswordCondition @SensitiveStrategyPassword private String fooPassword; //其他方法}测试/* * 自定义注解测试 /@Testpublic void customAnnotationTest() { final String originalStr = “CustomPasswordModel{password=‘hello’, fooPassword=‘123456’}”; final String sensitiveStr = “CustomPasswordModel{password=’’, fooPassword=‘123456’}”; CustomPasswordModel model = buildCustomPasswordModel(); Assert.assertEquals(originalStr, model.toString()); CustomPasswordModel sensitive = SensitiveUtil.desCopy(model); Assert.assertEquals(sensitiveStr, sensitive.toString()); Assert.assertEquals(originalStr, model.toString());}构建对象的方法如下:/** * 构建自定义密码对象 * @return 对象 */private CustomPasswordModel buildCustomPasswordModel(){ CustomPasswordModel model = new CustomPasswordModel(); model.setPassword(“hello”); model.setFooPassword(“123456”); return model;} ...

January 18, 2019 · 1 min · jiezi

Zilliqa网络拓扑

本文于2019年1月10日,在Zilliqa论坛(forum.zilliqa.com)首发。节点类型Zilliqa网络上有5种类型的节点:注:交易类型(类型I,II,III)的解释在这篇博客里(https://blog.zilliqa.com/prov…)分片节点(Shard nodes)处理类型I和类型II的分片内交易,并将共同签名的微型TX块提交给目录服务(DS)委员会。它们根据当前DS周期(DS epoch)内贡献和被接受的签名数量获得奖励。目录服务节点(Directory Service nodes)处理组装由分片提交的微TX-块。它们还处理类型II和类型III交易,这些交易是跨分片交易用来创建另一个微块。一旦组装完成后,它们将形成一个共同签名的最终TX块,并将其广播到Zilliqa网络中的所有节点。DS节点还基于在当前DS周期(DS epoch)期间贡献和接受的签名的数量来奖励。注意:在猫山王测试网中,一个DS Epoch目前包含100个TX Epoch。关于Epoch架构的更多细节在可以这里(https://github.com/Zilliqa/Zi…)找到。查找节点(lookup nodes)处理将交易分派到正确的分片,并协助种子节点获取状态和交易记录。他们赚取5%的交易费和coinbase奖励。种子节点 (seednodes) 帮助将交易转发到查找节点,通过提供DS块历史记录来帮助新的验证节点加入网络,并公开交易的API以允许浏览器/钱包发送交易并获取历史交易数据。它们共享由查找节点获得的奖励。档案库存储器(archival storage)在每个DS 周期(DS epoch)从种子节点获取历史数据,包括交易和块,并将它们存储在levelDB中。它们还为新加入的种子节点提供历史数据。网络图草图说明:S1,S2,S3:分片(Shards)DS:目录服务委员会(Directory service committee)Tx:交易(Transaction)MB:微块(Microblock)SD:状态变量(State delta)FB:最终块(Final block)FSD:最终状态变量(Final state delta)

January 17, 2019 · 1 min · jiezi

GitHub 为什么免费了

本文作者:张海龙,CODING 创始人兼 CEOGitHub 免费了,广大程序员喜大普奔。很多人关心 GitHub 这个操作会不会影响我们。首先说结论,对我们没有影响。而且,在目前这个 toB 的大时代前提下,这个操作是必然趋势。下面来跟大家分析一下。免费策略背后的商业基础看到很多人在说:“抱了微软爸爸大腿之后就是财大气粗。”这似乎也是许多人的认知,拿了投资就可以做福利了。我们在拿了腾讯的投资以后,很快跟腾讯云一起推出了腾讯云开发者平台,个人版的产品也彻底免费了。这个背后的逻辑其实是一致的,而且并不仅仅是做福利。我们先讲一个广为流传的概念—— Commoditize your Complement。因为暂时没有标准翻译,我们暂且称之为互补品策略。互补品策略是现代经济环境中,科技类公司的一个经典策略。具体体现为,在一个供给体系里面,在快速占有自身业务市场份额的同时,尽可能地将上下游环节的溢价降到最低,从而更有效地为自己的产品获取用户的剩余价值和增加在潜在用户前的曝光度。 这句话很晦涩,看不懂没关系,接着往后看。 2002 年,前微软的产品经理 Joel Spolsky 在他的文章 Strategy Letter V: The Economics of Open Source 里讨论过在这个在科技公司,尤其是软件/服务类公司中尤为明显的现象。任何在市场中的产品、服务或者软件其实都存在相对应的替代品(Substitutes)和互补品(Complement)替代品(Substitutes)指的是当用户觉得你的产品价格太贵而考虑其他的其他产品,比如猪肉就是牛肉的替代品,如果你是卖猪肉的,当牛肉价格上涨的时候,很多用户就会来买猪肉代替,你的销量就会上升。互补品(Complement)指的是跟你的产品一起购买的相互补充的产品,比如汽油就是汽车的互补品,典型的例子比如操作系统和电脑硬件也是互补品,当电脑的价格下降的时候,操作系统的销量就会上升。对于公司而言,主要的战略方向就是尽可能地让自身核心产品相对应的互补品的价格降低,如果你有能力让互补品的价格降低,你将能获得更多销量和收入并享受由于互补品受众扩大带来的曝光和热度。当 IBM 在设计 PC 的系统架构的时就大量选择了标准化的配件,并在那本传奇的 IBM-PC Technical Reference Manual 中详细的记录各个配件之间的规格和接口标准。这样其他的供应商就可以很省事儿地参与 PC 配件的设计和生成。结果就是有大量低价的内存条、硬盘、显卡等配件涌入市场,大大丰富了 PC 的配件市场。对于 IBM 来说,他们的目标就是扩充配件市场,因为配件和 PC 为互补品,低价的配件会很好地促进 PC 的销量。同样的招数微软用过。当微软最初将 PC-DOS 操作系统授权给 IBM 的时候,通过谈判打消了 IBM 希望独家授权的要求。微软此举的目的是消除 PC 之间的溢价,扩充 PC 市场,之后的故事我们就很清楚了,PC 的价格持续走低,开始被越来越多的用户接受,Windows 操作系统逐步变成全球最受欢迎的操作系统,微软估值一路起飞直到成为全球最值钱的公司。那现在我们了解了这个策略之后,再回头来看 GitHub 将私有仓库免费的策略,就能读懂此举背后的商业逻辑了。GitHub 为什么免费了?首先我们来看一下 GitHub 之前的策略。免费的公开仓库和付费的私有仓库:通过提供免费的公开仓库和搭建开源社区来为付费的私有仓库和企业服务提供更大的用户基数。根据 2016 年,bloomberg 报道的 GitHub 财务情况也可以很清楚的看出这个逻辑。从图上我们可以看出在个人版的私有仓库业务收入占到了总收入将近 13% 的比例。这也是为什么在 GitLab、Bitbucket 等竞品纷纷推出免费的私有仓库的时候,GitHub 还在坚持收费的原因,因为企业版和私有仓库并没有那么明显的互补关系,即使将私有仓库免费,扩大用户,也不能直接促进企业版的收入。 虽然都是给开发者用的产品,但是个人用跟公司用是完全不同的两件事。这里面其实是两个市场,两类用户,两种场景。把个人版用户转化为企业版用户是极低概率的事情。这也是为什么 CODING 要彻底区分个人版和企业版的原因。但是现在为什么又把私有仓库免费了呢?这要从去年 10 月微软完成收购 GitHub 的事件说起了,当时我写了一篇文章《微软的野望,GitHub 的长歌》阐述微软收购 GitHub 这件事对双方的意义。其中提到了一个很重要的观点“微软收购 GitHub,目标是通过 GitHub 这一软件开发的入口,获取更多的企业级云服务的客户,以及将 GitHub 售卖给微软现有的客户群体。”我们仔细分析 Github 免费仓库的细则:3 人/项目 的人数限制、私有项目无法使用保护分支、持续集成等高级功能。这是一个非常明显的针对于团队使用的限制,某种程度上,可以说 GitHub 放开了开发者个人的使用限制,但是对于公司和团队来说,基本没有任何变化。同时,微软改名部今年又将 TSVS 更名为“Azure DevOps” 进行包装与推出。GitHub - Azure Devops - Microsoft Azure 的路径逐渐明了。GitHub 与我们预想的一般,在微软的体系里成为其下游产品的互补品(Complement)。GitHub 13% 的总收入,对于微软来说,是可以舍弃的部分,微软真正在乎的是是否能因此构建出以 GitHub 为入口,DevOps 产品为切入点的微软云服务生态。GitHub 正逐渐走入微软的产品和生态体系中,这对于被收购的 GitHub 来说,也是必须达成的目标。未来会怎样?自从微软宣布收购 GitHub 之后,我们对 GitHub 私有仓库免费这个策略是有预期的,并且这件事情的落地比我们想象中的要晚。Gitlab 被 Google 和 Facebook 旗下基金投资,GitHub 被微软收购,CODING 被腾讯云战略投资。SaaS 及 PaaS 产品与基础云的合作的趋势已经越来越明显。SaaS 需要依赖基础云服务厂商的生态资源以扩充自己的产品可能性,基础云服务厂商需要 SaaS 产品为载体为开发者以及研发团队提供更便捷的计算服务。基于同样的逻辑, CODING 在 2018 年 也和战略投资方腾讯云联合推出了腾讯云开发者平台:dev.tencent.com。腾讯云开发者平台是全面免费的。舍弃 CODING 个人版的收入对于腾讯云来讲是完全可以接受的,我们需要构建的是一站式的云端开发生态,商业模型是建立在 toB 的市场上的,而不是向个人开发者收费。GitHub 在开源这件事情上已经做到全世界垄断了,而且在可见的未来都不会有挑战者,但是通过开源吸引来的将近 3000 万开发者,并没有给它带来商业上的成功。但是正如上文所说的微软云,Azure DevOps 给出了另外一条商业化路径。往前看十年,软件开发工具是一个生意,却不算是一个行业,真正商业化的玩家并不是很多,也不是很受重视。但是伴随着云时代的来临,我们发现,许多工具都可以云服务化。同时,在数字化转型的大背景下,企业对交付速度和创新的要求达到一个前所未有的高度,于是新的故事又产生了。在企业进行数字化转型的大驱动下,DevOps 工具的诉求会井喷式的产生,特别是传统的 IT 研发团队。但是通过需要安装配置的本地软件去满足这样的诉求效率太低,成本也比较高,而云和 SaaS 的出现解决了这个问题,所以 DevOps 工具也必须云服务化。可以预见的是,云厂商将不断完善通用的基础设施,包括 SaaS 形态的 DevOps 工具。微软在这个领域的积累,再加上 GitHub 的生态补充,我相信会是跑的最快的一个。再看看国内的云厂商,腾讯云,阿里云,华为云都在做相关的布局和开发。云计算的竞争已经从资源能力的竞争上升到业务能力的竞争了。GitHub 对个人用户免费只是一个前阵,对于开发者来讲,这样的变化肯定是好事。工具免费了,效率提高了。我们依然坚信云端开发的时代将要来临,软件开发的效率将会翻倍的提高,并且门槛会降低。点击链接,了解更多资讯。Reference:Joel on Software: And on Diverse and Occasionally Related Matters That Will Prove of Interest to Software Developers, Designers, and Managers, and to Those Who, Whether by Good Fortune or Ill Luck, Work with Them in Some Capacityhttps://medium.com/@moritzpla…https://www.bloomberg.com/new… ...

January 17, 2019 · 1 min · jiezi

最强NLP模型BERT可视化学习

摘要: 最强NLP模型谷歌BERT狂破11项纪录,全面超越人类,本文通过可视化带你直观了解它。2018年是自然语言处理(Natural Language Processing, NLP)领域的转折点,一系列深度学习模型在智能问答及情感分类等NLP任务中均取得了最先进的成果。近期,谷歌提出了BERT模型,在各种任务上表现卓越,有人称其为“一个解决所有问题的模型”。BERT模型的核心思想有两点,对推动NLP的发展有着重要的作用:(1)Transformer结构;(2)无监督的预训练。Transformer是一个只基于注意力(Attention)机制的序列模型,《Attention is all you need》一文中指出,它摒弃了固有的定式,没有采用RNN的结构。BERT模型同时需要预训练,从两个无监督任务中获取权重:语言建模(给定左右上下文,预测丢失的单词)以及下一个句子预测(预测一个句子是否跟在另一个句子后面)。BERT是个“多头怪”BERT与传统的注意力模型有所不同,它并非在RNN的隐藏状态上直接连接注意力机制。BERT拥有多层注意力结构(12层或24层,取决于模型),并且在每个层(12层或16层)中都包含有多个“头”。由于模型的权重不在层与层之间共享,一个BERT模型相当于拥有24×16=384种不同的注意力机制。BERT可视化BERT模型较为复杂,难以直接理解它学习的权重的含义。深度学习模型的可解释性通常不强,但我们可以通过一些可视化工具对其进行理解。Tensor2Tensor提供了出色的工具对注意力进行可视化,我结合PyTorch对BERT进行了可视化。点击查看详情。该工具将注意力可视化为连接被更新位置(左)和被关注位置(右)之间的连线。不同的颜色对应不同的“注意力头”,线段宽度反映注意力值的大小。在该工具的顶部,用户可以选择模型层,以及一个或者多个“注意力头”(通过点击顶部颜色切换,一共包含12个不同的“头”)BERT到底学习什么?该工具能用于探索预先训练的BERT模型的各个层以及头部的注意模式。以下列输入值为例进行详解:句子A:I went to the store.句子B:At the store, I bought fresh strawberries.BERT采用WordPiece tokenization对原始句子进行解析,并使用[CLS]对token进行分类以及[SEP]对token进行分隔,则输入的句子变为:[CLS] i went to the store. [SEP] at the store, i bought fresh straw ##berries. [SEP]接下来我将确定6个关键模式,并展示每个模式特定层/头的可视化效果。模式1:下一个单词的注意力(Attention to next word)在该模式下,特定单词的大部分注意力都集中在序列中该单词的下一个token处。如下图所示,我们以第二层的head 0为例(所选头部由顶部颜色栏中突出显示的正方形表示)。左边图中展示了所有token的注意力,右边则显示了特定token(“i”)的注意力。“i”几乎所有的注意力都集中在它的下一个token,即“went”处。左图中,[SEP]指向了[CLS],而非“at”,也就是说,指向下一个单词的这种模式只在句子中起作用,而在句子间的效果较弱。该模式类似于RNN中的backward,状态从右往左依次更新。模式2:前一个单词的注意力(Attention to previous word)在该模式下,特定单词的大部分注意力都集中在序列中该单词的前一个token处。本例中,“went”的大部分注意力集中于它的前一个单词“i”。模式2不如模式1明显,特定的单词注意力有所分散。该过程与RNN中的forward类似。模式3:相同/相关单词的注意力(Attention to identical/related words)在该模式下,特定单词的大部分注意力集中于与其相同或者相关的单词,包括该单词本身。下图中,“store”的大部分注意力集中在它本身。由于注意力有所分散,该模式也不明显。模式4:其它句子中相同/相关单词的注意力(Attention to identical/related words in other sentence)在该模式中,注意力集中在其它句子中与指定单词相同或者相似的单词。如下图,第二个句子中的“store”与第一个句子中的“store”关联最强。这对于下一个句子预测任务非常有帮助,它能够帮助识别句子之间的关系。模式5:预测单词的注意力(Attention)在该模式下,注意力集中于其它可以预测源单词的单词上,且不包括源单词本身。如下图,“straw”的注意力主要集中于“##berries”,而“##berries”的注意力主要集中于“straw”。模式6:分隔符标记的注意力(Attention to delimiter tokens)在该模式下,特定单词的注意力主要集中于分隔符,[CLS]或[SEP]中。如下图,大多数的注意力都集中在两个[SEP]中,这或许是模型将语句级别状态传递到各个token中的一种方法。本文作者:【方向】阅读原文本文为云栖社区原创内容,未经允许不得转载。

January 17, 2019 · 1 min · jiezi

vue-cli中vuex IE兼容

vue-cli中使用vuex的项目 在IE中会出现页面空白 控制台报错的情况:我们只需要安装一个插件,然后在main.js中全局引入即可安装 npm install –save-dev -polyfill引入 import ‘babel-polyfill’

January 16, 2019 · 1 min · jiezi

Pro Git 学习笔记

Pro Git 学习笔记文档地址:Pro Git原文地址:PRO GIT 学习笔记1、Git起步初次运行Git前的配置用户信息git config –global user.name “your user name"git config –global user.email “your email address"文本编辑器设置默认的文本编辑器:git config –global core.editor emacs查看配置信息git config –list2、Git基础创建Git仓库在工作目录中初始化新仓库git init克隆现有仓库git clone urlurl分ssh和https两种,推荐使用ssh。检查当前文件状态git status跟踪最新文件git add 文件名或*.js/css/html…或.忽略不想提交的文件cat .gitignore查看已暂存和未暂存的更新git diffgit diff –cached提交更新git commit -m “提交备注信息"跳过使用暂存区域git commit -a “提交备注信息"在提交时使用git commit -a就会把已跟踪的已暂存文件一起提交,跳过git add步骤,即两个命令进行合并。移除文件git rm从已跟踪文件清单中移除并删除工作目录中的指定文件,先使用git status查看跟踪文件清单,再使用git rm进行精准移除。强制移除使用git rm -f,但不推荐使用。从远程仓库中删除文件,使用:git rm –cached 文件名/.文件后缀/文件夹…移动文件对文件重命名或移动文件,可以使用:git mv file_from file_to查看提交历史git loggit log -p -2-p选项展开显示每次提交的内容差异,用-2显示最近的两次更新。单词层面的对比,使用:git log -p -U1 –word-diff这个命令在代码检查中较少使用,在图文编辑中出现较多。显示摘要信息,使用:git log –stat其他有用的命令:–pretty选项可以指定使用完全不同于默认格式的方式展示提交历史,用oneline将每个提交放在一行显示,这在提交数很大时非常有用:git log –pretty=onlineformat可以定制要显示的记录格式:git log –pretty=format:"%h - %an, %ar : %s"常用的格式占位符写法及其代表的意义选项说明%H提交对象(commit)的完整哈希字串%h提交对象的简短哈希字串%T树对象(tree)的完整哈希字串%t树对象的简短哈希字串%P父对象(parent)的完整哈希字串%p父对象的简短哈希字串%an作者(author)的名字%ae作者的电子邮件地址%ad作者修订日期(可以用-date=选项定制格式)%ar作者修订日期,按多久以前的方式显示%cn提交者(committer)的名字%ce提交者的电子邮件地址%cd提交日期%cr提交日期,按多久以前的方式显示%s提交说明添加ASCII字符串表示的简单图形git log –pretty=format:"%h %s” –graphgit log 命令支持的选项选项说明-p按补丁格式显示每个更新之间的差异。–word-diff按 word diff 格式显示差异。–stat显示每次更新的文件修改统计信息。–shortstat只显示 –stat 中最后的行数修改添加移除统计。–name-only仅在提交信息后显示已修改的文件清单。–name-status显示新增、修改、删除的文件清单。–abbrev-commit仅显示 SHA-1 的前几个字符,而非所有的 40 个字符。–relative-date使用较短的相对时间显示(比如,“2 weeks ago”)。–graph显示 ASCII 图形表示的分支合并历史。–pretty使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)。–oneline–pretty=oneline –abbrev-commit 的简化用法。限制输出长度按照时间作限制的命令:–since和–untilgit log –since=2.weeks搜索条件–author 显示指定作者的提交–grep 搜索提交说明中的关键字–all-match 同时满足这两个选项搜索条件的提交其他常用的类似选项选项说明-(n)仅显示最近的 n 条提交–since, –after仅显示指定时间之后的提交。–until, –before仅显示指定时间之前的提交。–author仅显示指定作者相关的提交。–committer仅显示指定提交者相关的提交。具体示例:git log –pretty="%h - %s” –author=gitster –since=“2018-10-01” \ –before=“2008-11-01” –no-merges – t/撤消操作修改最后一次提交git commit –amend取消已经暂存的文件git reset HEAD 文件名取消对文件的修改这条命令谨慎使用git checkout – 文件名远程仓库的使用查看当前的远程库git remote显示对应的克隆地址git remote -v添加远程仓库git remote add [shortname] url抓取仓库信息git fetch [shortname]从远程仓库抓取数据此命令会从远程仓库抓取数据到本地git fetch [remote-name]抓取克隆的远程仓库的更新数据git fetch originfetch命令只是把远程仓库的数据抓取到本地,并不会自动合并到当前工作分支推荐使用的拉取远程仓库数据,并进行数据合并操作的命令git pull推送数据到远程仓库git push origin master查看远程仓库信息git remote show [remote-name]远程仓库的删除和重命名重命名远程仓库git remote renamegit remote rename vue react移除远程仓库git remote rm vue打标签显示已有的标签git tag设定条件进行搜索git tag -l “v1.4.2.“新建标签轻量级标签git tag含附注的标签git tag -agit tag -a v1.4 -m “my version 1.4"查看相应标签的版本信息git show v1.4签署标签git tag -sgit tag -s v1.5 -m “my signed 1.5 tag"验证标签git tag -v [tag-name]git tag -v v1.4.2.1后期加注标签忘记了加注标签,只要在打标签的时候跟上对应提交对象的校验和即可git tag -a v1.2 9fceb02分享标签git push origin v1.5一次推送所有本地新增标签git push origin –tags技巧和窍门自动补全windows系统下连续按Tab键Git 命令别名git config –global alias.ci commitgit config –global alias.st statusGit 分支新建testing分支git branch testing切换到testing分支git checkout testing分支的新建与合并以上两个命令进行合并git checkout -b testingGit会把工作目录的内容恢复为检出某分支时它所指向的那个提交对象的快照。它会自动添加、删除和修改文件以确保目录的内容和当时提交时完全一样。合并提交内容git mergegit checkout mastergit merge hotfix删除工作分支git branch -d hotfix分支的合并查看冲突 git status调用可视化的合并工具解决冲突git mergetool分支的管理查看各个分支最后一个提交对象的信息git branch -v查看哪些分支已被并入当前分支git branch –merged查看尚未合并的分支git branch –no-merged利用分支进行开发的工作流程长期分支特性分支远程分支同步远程服务器上数据到本地git fetch origin 推送本地分支git push origin master在远程分支上分化出新的分支:git checkout -b serverfix origin/serverfix跟踪远程分支git checkout -b sf origin/serverfix删除远程分支分支的衍合整合分支方法git mergegit rebase从一个特性分支中再分出一个特性分支的历史git rebase –onto master server clientgit checkout mastergit merge server衍合的风险一旦分支中的提交对象发布到公共仓库,就千万不要对该分支进行衍合操作。服务器上的 Git协议Git可以四种主要的传输协议进行数据传输:本地协议、SSH协议、Git协议和HTTP协议。在服务器上部署 Gitgit clone –bare my_project my_project.git把裸仓库移到服务器上未完待续… ...

January 16, 2019 · 2 min · jiezi

深度学习为图片人物换装【python代码教程】

在观看本文之前,请答应我要善良。昨天预告了下,发现很多同学对这个模型都表示出兴趣,甚至有好多同学后台发来照片让我帮他们脱裤子。授人以鱼不如授人以渔,请这些同学好自为之~01.效果演示本文案例使用的是开源项目instagan,是一种比较新的gan模型建模原理,来自2019年ICLR的论文,下面看下效果对照:(出于人道主义,会把人物的长裤脱掉然后换上短裙)02.环境配置首先玩这个模型需要两个前提条件:有梯子python3.6版本有GPU环境(因为源代码是要求必须在GPU的Cuda环境下运行,如果没有GPU的同学推荐用PAI里面的DSWhttps://data.aliyun.com/product/learn ,比较便宜 )(1)下载实验代码:https://github.com/sangwoomo/instagan(2)下载pre-trained model,如果只是实验就直接用训练好的模型即可:https://drive.google.com/drive/folders/1xb9rR21MhMVselc6HTmOr73WOkOviFmO(如果只玩换裤子这个实验,下载pants2skirt_mhp_instagan这个模型即可)(3)安装代码中的requirement.txt中的依赖包最后把下载的model文件放到代码文件的根目录下,目录结构如下(蓝色部分为模型文件夹,里面是200_net_G_A.pth和200_net_G_B.pth):完成以上步骤,整个环境就搭建好了。03.使用模型做图片转换注:这个项目的代码有很多hard code的逻辑,需要严格按照下面的做法执行才有可能跑通,包括所有文件的命名。(1)先设置需要转换的图片在datasets目录下新增一个test文件夹,构建如下的文件格式testA和testB存放需要转换的原图,类似于“效果演示中”穿着长裤的图片,testA_seg和testB_seg需要存放mask图片。mask文件是转换图片中的裤子样式,如下图对应实例图片左边的女生裤子(如果想转换其它部位,就不用我举例子了吧):mask图片还需要与被转换图片命名一致,详细规则参见源代码中的datasets,如果不一致会出现以下错误:https://github.com/sangwoomo/instagan/issues/5(2)执行图片转换逻辑在工程的根目录下执行以下代码,使用工程下的test.py这个测试代码: python test.py –dataroot ./datasets/test –model instagan –name pants2skirt_mhp_instagan –loadSizeH 240 –loadSizeW 160 –fineSizeH 240 –fineSizeW 160 –ins_per 2 –ins_max 20如果没有报错的话在GPU环境下1分钟就可以执行完毕,代码执行完毕后在工程的results文件夹下就能看到转换好的图片了。04.总结整个项目的效果还是不错的,不过可能代码还没来得及优化,可能在实验的过程中有很多坑,大家需要一定的debug能力才能把工程跑起来,小白不太建议使用哈。本文作者:傲海阅读原文本文为云栖社区原创内容,未经允许不得转载。

January 16, 2019 · 1 min · jiezi

Travis CI + github + hexo 自动化部署

Travis CI是目前新兴的开源持续集成构建项目,采用yaml格式,简洁清新独树一帜。目前大多数的github项目都已经移入到Travis CI的构建队列中。Travis-CI会同步你在GitHub上托管的项目,每当你commit push成功之后,就可以根据配置文件进行项目的构建发布。作为一名苦逼(qiongsuan)程序员,想自己搞点什么,又苦于现在什么都收费,于是乎,有了本篇文章,满足了广大和我一样,囊中羞涩却又按耐不住一颗躁动的心的开发者们之前用hexo搭建了一个博客,挂载在自己的github上,之所以选择这两者,因为他们都是开源的(其实为了免费),也因此,github域名无法在百度等搜索引擎备案,我的博客张吉成的博客主页也是毫无访问量可言T_T。之前每次部署都是最基本的手动编译,打包,上传github,搞过一段时间的jenkins(有兴趣的朋友可以移步我的博客),由于没有自己的服务器,每次自己的电脑都要开着作为服务器,实在麻烦。后来无意间发现了Travis,功能强大且配置简单,还是免费的,简直完美,唯一的缺点是只支持github项目。本文记录了配置Travis的全过程,构建步骤为:本地开发完成,提交代码到github仓库;github收到提交的更新,通知Travis;Travis 收到github的提交通知,进行构建;hexo 的安装使用本文就不做介绍了,可以参考之前的文章hexo常用命令,hexo创建文章&文章缩略图及banner&MarkDown注册配置 Travis打开Travis CI官网,进行注册,这里就不做太多赘述,推荐用github账户注册;绑定你的github账户,此时Travis会同步你的github仓库,将你要监听的github仓库名选中,此时Travis会监听该仓库的push操作,并执行指定的脚本文件;添加 .travis.yml当我们提交代码后执行的一系列操作都是在 .travis.yml文件中配置的;language: node_js #設置語言node_js: stable #設置相應的版本cache: apt: true directories: - node_modules # 緩存不經常更改的內容install: # 执行安装操作 …script: # 开始部署 …after_script: # 部署后操作 …branches: # 配置监听的分支 only: - master #只監測master分支,master是我的博客源碼分支的名稱,可根據自己情況設置env: # 环境变量配置 global: - GH_REF: github.com//blog.git #設置GH_REF,注意更改更详细的参数配置可以参考官网如下是我的配置信息:language: node_js #設置語言node_js: stable #設置相應的版本cache: apt: true directories: - node_modules # 緩存不經常更改的內容before_install: - echo 安装hexo相关环境…install: # - npm install -g cnpm –registry=https://registry.npm.taobao.org - cnpm installbefore_script: - echo 正在清空缓存静态文件… - hexo clean # 清除缓存静态文件 - echo 正在生成静态文件… - hexo g # 生成静态文件 - cd ./public - ls -lscript: - echo 开始部署… - git init - git config –global user.name “${GH_username}” # 修改name - git config –global user.email “${GH_useremail}” # 修改email - git add ./ - git commit -m “update” - git push –force –quiet “https://${GH_TOKEN}@${GH_REF}” master:master # GH_TOKEN是在Travis中配置token的名稱after_script: - echo 部署完成!branches: only: - master # 只監測master分支,master是我的博客源碼分支的名稱,可根據自己情況設置env: global: - GH_REF: github.com/<prourl> # 设置 github 项目仓库地址 - GH_username: <yourname> # 设置 github 用户名 - GH_useremail: <youremail> # 设置 github 绑定邮箱地址生成github access Token 并 配置到 Travis此步骤是为了使travis获得对github的操作权限,如git push等生成github access Token如上图所示,登陆github并打开该页面,并新建token如上图进行对应操作,生成token,注意token只显示一次,要保存好备用。配置 access token 到 Travis打开Travis CI 找到setting页面,填写对应的token名及上面步骤生成的token值,如下图:细心的同学可能会发现我的 .travis.yml 文件中有下面这样一段配置after_script: - echo 开始部署… - cd ./public - git init - git config –global user.name “yourname” #修改name - git config –global user.email “youremail” #修改email - git add ./ - git commit -m “update” - git push –force –quiet “https://${GH_TOKEN}@${GH_REF}” master:master #GH_TOKEN是在Travis中配置token的名稱 - echo 部署完成!branches: only: - master #只監測master分支,master是我的博客源碼分支的名稱,可根據自己情況設置env: global: - GH_REF: github.com/yourname/bolg.git #設置GH_REF,注意更改yourname其中有两个变量,GH_REF是在env中配置的,而GH_TOKEN则是我们刚刚在设置中添加的github token,此时执行git push –force –quiet “https://${GH_TOKEN}@${GH_REF}” master:master命令就可以在不用输入用户名密码的情况下进行提交。测试提交代码到github中查看部署情况至此,整个部署完成,赶快自己尝试一下吧! ...

January 16, 2019 · 2 min · jiezi

有哪些鲜为人知,但是很有意思的网站?

扩展阅读有哪些鲜为人知,但是很有意思的网站?一份攻城狮笔记每天搜集 Github 上优秀的项目一些有趣的民间故事超好用的谷歌浏览器、Sublime Text、Phpstorm、油猴插件合集工具类看图识花(上传图片识别花的种类):http://stu.iplant.cn/webGridzzly(在线制作自己的网格纸):http://www.gridzzly.com/在线电子书转换器(电子书格式在线转换):http://cn.epubee.com/字体转换器(字体在线转换):http://www.akuziti.com/证件照换底色(一键换底色):https://www.bgconverter.com/OPEN GPS(高精度 IP 定位):https://www.opengps.cn/Default.aspxWindy(在线气象观测):https://www.windy.com/RAMMB(全球实时卫星云图):http://rammb-slider.cira.colostate.eduASD 商品历史价格查询(商品价格曲线):http://asd-price.com/铁路信息查询(全国铁路车站车次信息查询):https://moerail.ml/DesignCap(免费海报在线制作):https://www.designcap.com/app/在线工具箱(各种实用工具聚合):http://tool.mkblog.cn/在线工具箱(各种实用工具聚合):http://www.nicetool.net/长链接生成器:长链接生成器 v2.2图片/视频工具Photopea(网页版 PS):https://www.photopea.com/PHOTOMOSH(给图片视频加特效):https://photomosh.com/Algorithmia(AI 给黑白照片上色):https://demos.algorithmia.com/colorize-photos/Remove.bg(在线一键抠图):https://www.remove.bg/Nod to the Rhythm(让你的照片张嘴唱歌):http://nodtotherhythm.com/makeGfycat(在线制作并托管高清 GIF):https://gfycat.com/应景图(GIF 图片添加字幕水印):http://www.yingjingtu.com/index极速瘦图(图片压缩):http://www.jsysuo.com/Picdiet(图片压缩):https://www.picdiet.com/zh-cnTinyPNG(图片压缩):https://tinypng.com/Squoosh(图片压缩):https://squoosh.app/Needs More JPEG(图片超级压缩):http://needsmorejpeg.com/Waifu2x(图片在线无损放大):http://waifu2x.udp.jp/Bigjpg(图片放大):http://bigjpg.com/ILOVEIMG(在线图片编辑器):https://www.iloveimg.com视频解析网(微博,秒拍,快手,抖音):https://www.parsevideo.com/IT/AI/工具类王斌给您对对联(AI 在线对对联):https://ai.binwang.me/couplet/玄派网 (武侠生成器):http://www.xuanpai.com/Grabient(CSS 代码渐变颜色生成工具):https://www.grabient.com/多彩的颜色(图片色彩分析):https://woshizja.github.io/colorful-color/Emoji 短网址(把网址变成 Emoji 表情):https://e.mezw.com/表情符号生成器(Emoji 表情自定义创建生成):https://phlntn.com/emojibuilder/万象智能鉴黄系统(图片分析):https://cloud.tencent.com/act/event/ci_demo给小动物加光剑(趣味加工):https://giphy.com/search/lightsaber-catsLyrebird(克隆自己的声音,需登录):https://lyrebird.ai/signupWindows93(网页版 windows93 系统):http://www.windows93.net/百度镜子(百度的镜子网站):https://baidujingzi.com/bilibili 镜子(bilibili 的镜子网站)http://www.ilidilid.com/ertdfgcvb(代码特效展示):https://ertdfgcvb.xyz/Silk(互动生成艺术画):http://weavesilk.com/ASCIIFlow Infinity(可视化字符图像绘制):http://asciiflow.com/我知道你下载了什么(BT 下载内容监测):https://iknowwhatyoudownload.com/en/peer/Foxmiguel(游戏直播聚合站):http://www.foxmiguel.com/逗比拯救世界(表情包分享):http://www.bee-ji.com/装逼大全(表情包制作):https://www.zhuangbi.info/音乐/影视类穿帮网(影视剧穿帮镜头赏析):http://www.bug.cn/The Movie title stills collection(电影标题剧照集):http://annyas.com/screenshots/音乐搜索器(音乐聚合搜索下载生成外链):http://tool.mkblog.cn/music/资源帝(在线音乐聚合)http://music.ziyuandi.cn/SUKIER(冷门歌曲推荐):http://www.sukier.com/中国摇滚(中国摇滚年代史):http://www.yaogun.com/Youtube 字幕下载(字幕下载):http://downsub.com/胖鸟电影(最新影视剧蓝光下载站):http://www.pniao.com/爱追剧(电视剧电影追剧下载):http://www.aizhuiju.com/Mov电影天堂(小体积影视剧下载):https://www.dygod.net/高清 MP4ba(最新影视剧下载:复活了):http://www.mp4ba.com/RARBG(美国影视 BT 站):https://rarbgprx.org/torrents.php动漫花园资源网(动漫作品 BT 下载站):https://share.dmhy.org/爱恋动漫(动漫作品 BT 下载站):http://www.kisssub.org/迷你 MP4(最新影视剧下载):http://www.minimp4.com二次元/动漫类萌娘百科:https://zh.moegirl.org/Mainpage神奇宝贝百科:http://wiki.52poke.com/小鸡词典(互联网流行词汇百科网站):https://jikipedia.com/AnimeShot (把动画字幕用于吐槽生活):https://as2.bitinn.net/萌兔本地漫画阅读器(漫画本子在线阅读):http://wusagi.pw/猫耳 FM(二次元声站):https://www.missevan.com/Bilibili 工具箱(弹幕内容查用户名):https://biliquery.typcn.com/PaintsChainer(AI 为你的画自动上色):AI 涂色绘画文艺类时间胶囊(封存自己的记忆):http://p.timepill.net/时光邮局(给未来的自己写一封信):https://www.hi2future.com/I Remember(我记得,一个记忆碎片网站,头脑特工队):http://i-remember.fr/en/海の見える駅(能看见海的车站):https://seaside-station.com/网盘/搜索类快速创建收件夹(百度网盘匿名收件箱):http://xzc.cn/Firefox Send(临时网盘):https://send.firefox.com/Ecosia(搜索引擎,搜索使用的广告收入用于种植树木):https://www.ecosia.org/爱搜资源(百度网盘密码破解分享):https://www.aisouziyuan.com/鸠摩搜书(电子书搜索下载):https://www.jiumodiary.com/GIPHY(GIF 动图搜索网站):https://giphy.com/Similar Site Search(相似网站搜索):https://www.similarsitesearch.com/cn/文学/百科类武侠世界(歪果仁翻译中国小说):https://www.wuxiaworld.com/维基大典(国学百科):維基大典Gallerix(世界名画档案馆):https://gallerix.ru/Internet Archive(互联网档案博物馆):https://archive.org/世界护照大全(领略全球护照风采):https://www.passportindex.org/cn/中国海报(中国历年海报存档):https://chineseposters.net/美丽的化学(化学之美):https://www.beautifulchemistry.net/乡音苑(中国方言活地图):http://phonemica.net/湿在人为(两性博客):http://www.idashi.org/汉典(汉字典书):http://www.zdic.net/书格(传统书籍):https://shuge.org/古诗文网(中国传统古诗文):https://www.gushiwen.org/中少快乐阅读平台(怀旧少儿老杂志):少年儿童杂志全集实用/行政类国家邮政局申诉网站(快递问题投诉专用):http://sswz.spb.gov.cn/无人认领尸体在线查询(慎入):http://www.gzbz.com.cn/dead_men/index.asp药物临床试验登记与信息公示平台(人体实验):http://www.chinadrugtrials.org.cn/德州大学电子图书馆(人体骨骼 X 光标本):http://www.digimorph.org/index.phtml趣味/无聊类Spray.training(FPS 游戏压枪训练工具):http://spray.training/一键六学(网络梗生成器):http://bog.ac/tool/6/#今天中午吃什么?(世纪难题):https://www.zwcsm.com/你好污啊(撩妹金句):https://www.nihaowua.com/Clash(用歌声说出你想说的话,想起了《大黄蜂》):https://clash.me/Windows Update Prank(假装 Windows 升级界面):http://fakeupdate.net/字符跳跃(让你的网址动起来):http://glench.com/hash/#CLICKhappy happy hardcore(治愈小表情):https://happyhappyhardcore.com/经典 DVD(无聊网站):http://itneverhitsthecorner.com/Neave.TV(稀奇古怪的电视频道):https://neave.tv/无聊网站大全(点击进入随机无聊站):https://theuselessweb.com/声音/太空类雨季情绪(下雨的声音):https://rainymood.com/VirtOcean(海洋的声音):http://virtocean.com/Purrli(猫打呼噜的声音):https://purrli.com/这里有猫(Purrli 中文版):https://m.niucodata.com/cat/cat.php?from=wbMeteor showers(太空视角观看流星雨):https://www.meteorshowers.org/怀旧类秘密花园(中文网站考古):http://www.yini.org/Inspirograph(怀旧在线万花尺):https://nathanfriend.io/inspirograph/日本传统色(古典传统配色):http://nipponcolors.com/中国色(日本传统色的中文版):http://zhongguose.com/四大名著小说(名著地图):http://www.sdmz.net/水浒 108 将形象大全(怀旧图片):http://ls.ganquancun.com/shuihuzhuan108/游戏/测试类Am I pretty or ugly?(在线颜值分析):https://www.prettyscale.com/MyAccent(测试你的口音是英式还是美式):口音测试扫雷(网页版扫雷游戏):https://www.saolei.org/信任的进化(人性小游戏):https://www.sekai.co/trust/太鼓达人(日本经典音乐游戏网页版):https://taiko.bui.pm/一画换一画(互动绘画):http://www.sketchswap.com/Lines FRVR(划线小游戏):https://lines.frvr.com/Bad News(虚假新闻是怎样炼成的):https://getbadnews.com/#playLINE RIDER(Flash 小游戏):https://www.linerider.com/QWOP(经典小游戏):http://www.foddy.net/Athletics.htmlMikutap(鼠标音乐游戏):https://aidn.jp/mikutap/Mikutap(鼠标音乐游戏中文版):https://static.hfi.me/mikutap/it’s a(door)able(解锁小游戏):https://ncase.me/door/ScribblerToo(蜘蛛画画)Scribbler TooTexter(字符画):http://tholman.com/texter/Finding Home(音乐解压游戏):http://findingho.me/魔术键盘(解压网站):http://magickeyboard.io/Emojis & Earth Porn(寻找不动的表情):http://emojisandearthporn.com/Staggering Beauty(精神污染):http://www.staggeringbeauty.com/Pica Pic(复古手持游戏合集):http://www.pica-pic.com/在线 DOS 游戏(中文怀旧游戏):https://dos.zczc.cz/中文家用游戏博物馆(中文怀旧游戏):http://www.famicn.com/老男人游戏网(模拟器 ROM 下载网站):http://www.oldmanemu.net/Neave Interactive(互动小游戏合集):https://neave.com/Bestgames(在线小游戏网站):http://www.bestgames.com/IGCD(互联网游戏汽车数据库):http://www.igcd.net/网站之最第一个网站(世界上第一个网站):http://info.cern.ch/水滴(世界上最小的网站):http://www.guimp.com/世界最高(世界上最高的网站):https://worlds-highest-website.com/世界最长(世界上最长,增长最快的网站):http://www.worldslongestwebsite.com/世界之邮(世界上最长的邮箱):世界上最长的邮箱特别推荐福利吧(分享你的福利吧):http://fulibus.net/龙轩导航(可能是最好用的导航网站):http://ilxdh.com/抽屉新热榜(不正经的资讯社区):https://dig.chouti.com有趣网址之家(趣味小站集锦):https://youquhome.com/原文地址有哪些鲜为人知,但是很有意思的网站? ...

January 15, 2019 · 1 min · jiezi

使用 Github API 更新仓库

总览本文为大家提供一种使用 GitHub API 更新 GitHub 仓库的方法。通常我们会使用 Git 客户端 Commit 然后 Push 到 GitHub,但 GitHub 为我们提供了相关 API,在某些情况下可以直接通过 API 更新仓库。在此之前有一些前置知识点,你需要了解 git 的储存原理。碰巧最近掘金有一篇很火的文章有提到相关知识。使用 GitHub API 更新仓库只需六步。虽然对于第一次接触的新手来说可能会有点复杂,但是理清关系之后思路便会很清晰了。获取 Ref:Ref 指 Git 的引用。如果要发起 Commit,必须知道你的 Commit 要提交到什么地方去(Commit 是一个接一个的链状关系,所以需要知道上一个 Commit 的信息),这一步做的就是这件事。获取 Commit:用于获取当前 Commit 的 tree 的 sha。生成 Blob:相当于正常本地提交的 add 操作,可以参考这里的解释。生成 Tree:构建一个新的 tree,把上一步生成的 Blob 放到合适的位置。这里需要用到的参数 base_tree 就是来自第二步的 Commit 信息。生成 Commit:提交你的 tree,这步跟正常 commit 很接近,不过要你手动找到此次提交的上一次提交,借哈希值把新的 Commit 接起来。更新 Ref:使 master 的指针指到你最新提交的版本。注意:访问 POST 方法的接口必须带 token,虽然 GET 可以不带,但是会限制访问频率,所以建议都带上。相关文档:https://developer.github.com/v3/#authentication1. 获取 Ref 1.1 文档地址https://developer.github.com/v3/git/refs/#get-a-reference1.2 请求地址GET https://api.github.com/repos/ssshooter/test/git/refs/heads/master1.3 返回数据{ “ref”: “refs/heads/master”, “node_id”: “MDM6UmVmMTY0Nzk4NDczOm1hc3Rlcg==”, “url”: “https://api.github.com/repos/ssshooter/test/git/refs/heads/master", “object”: { “sha”: “cda66de943082033f4b761639df77728d7bca4f0”, “type”: “commit”, “url”: “https://api.github.com/repos/ssshooter/test/git/commits/cda66de943082033f4b761639df77728d7bca4f0" }}2. 获取 Commit2.1 文档地址https://developer.github.com/v3/git/commits/#get-a-commit2.2 请求地址GET https://api.github.com/repos/ssshooter/test/git/commits/cda66de943082033f4b761639df77728d7bca4f02.3 返回数据{ “sha”: “cda66de943082033f4b761639df77728d7bca4f0”, “node_id”: “MDY6Q29tbWl0MTY0Nzk4NDczOmNkYTY2ZGU5NDMwODIwMzNmNGI3NjE2MzlkZjc3NzI4ZDdiY2E0ZjA=”, “url”: “https://api.github.com/repos/ssshooter/test/git/commits/cda66de943082033f4b761639df77728d7bca4f0", “html_url”: “https://github.com/ssshooter/test/commit/cda66de943082033f4b761639df77728d7bca4f0", “author”: { “name”: “DeJavuJo”, “email”: “ssshooterx@gmail.com”, “date”: “2019-01-09T06:02:07Z” }, “committer”: { “name”: “GitHub”, “email”: “noreply@github.com”, “date”: “2019-01-09T06:02:07Z” }, “tree”: { “sha”: “d7a57d28ace0db12773c7d70675f94a48da6421f”, “url”: “https://api.github.com/repos/ssshooter/test/git/trees/d7a57d28ace0db12773c7d70675f94a48da6421f" }, “message”: “Create readme.md”, “parents”: [ ], “verification”: { “verified”: true, “reason”: “valid”, “signature”: “—–BEGIN PGP SIGNATURE—–\n\nwsBcBAABCAAQBQJcNY5fCRBK7hj4Ov3rIwAAdHIIABh8I89hATqg1mSYtpx1aY\nJt/woDobMO7FGE5qXO0NNrCMqwF6mdPJOMMMZvVWF1ULTm8ZJ52GAh5xAnPMZEnQ\n8tTjj/Qc0eZCm5B1xO66rgx+7eKkeGUPJj5bX7Lf0i9Se70Ff0jaCdH94RgiSL2d\nclDLhNyM4u6AH//k7S3Ud1O6ezXd4+99381Xa331PDLhJttUAbRFRCThszbcxRUT\nPvyhPNvXB5ug4J+tAMaZJvZEaED0c1k2Yx1+TYkLvPjOlqXvqaDVpMZieluD5l+f\nu13NAoCnLfBe0Skak/8MxRDbMcJYNW78ll60HAa6jJtElD8Vkek8WoVAZ8p3iIE=\n=xI87\n—–END PGP SIGNATURE—–\n”, “payload”: “tree d7a57d28ace0db12773c7d70675f94a48da6421f\nauthor DeJavuJo 1547013727 +0800\ncommitter GitHub 1547013727 +0800\n\nCreate readme.md” }}3. 生成 Blob3.1 文档地址https://developer.github.com/v3/git/blobs/#create-a-blob3.2 请求地址POST https://api.github.com/repos/ssshooter/test/git/blobs3.3 请求参数{ “content”: “Content of the blob”, “encoding”: “utf-8”}3.4 返回数据{ “sha”: “929246f65aab4d636cb229c790f966afc332c124”, “url”: “https://api.github.com/repos/ssshooter/test/git/blobs/929246f65aab4d636cb229c790f966afc332c124"}4. 生成 tree4.1 文档地址https://developer.github.com/v3/git/trees/#create-a-tree4.2 请求地址POST https://api.github.com/repos/ssshooter/test/git/trees4.3 请求参数注意:tree.path 可以写深层目录,如 deep/deep/newFile.md(前面不用写斜杠){ “base_tree”: “d7a57d28ace0db12773c7d70675f94a48da6421f”, // commit tree 的 sha “tree”: [ { “path”: “apiCommitFile.md”, // 文件路径 “mode”: “100644”, // 类型,详情看文档 “type”: “blob”, “sha”: “929246f65aab4d636cb229c790f966afc332c124” // 刚才生成的 blob 的 sha } ]}4.4 返回数据{ “sha”: “2b3a65095ceb7cf4425f52d59ca5974d826cff80”, “url”: “https://api.github.com/repos/ssshooter/test/git/trees/2b3a65095ceb7cf4425f52d59ca5974d826cff80", “tree”: [ { “path”: “readme.md”, “mode”: “100644”, “type”: “blob”, “sha”: “b1b716105590454bfc4c0247f193a04088f39c7f”, “size”: 5, “url”: “https://api.github.com/repos/ssshooter/test/git/blobs/b1b716105590454bfc4c0247f193a04088f39c7f” }, { “path”: “tree”, “mode”: “040000”, “type”: “tree”, “sha”: “91d8d59147e395effaeacd01c9d8553b37cf77c5”, “url”: “https://api.github.com/repos/ssshooter/test/git/trees/91d8d59147e395effaeacd01c9d8553b37cf77c5” } ], “truncated”: false}5. 生成 Commit5.1 文档地址https://developer.github.com/v3/git/commits/#create-a-commit5.2 请求地址POST https://api.github.com/repos/ssshooter/test/git/commits5.3 请求参数{ “message”: “a Commit with GitHub api”, “parents”: [ “cda66de943082033f4b761639df77728d7bca4f0” // 上次 commit 的sha ], “tree”: “2b3a65095ceb7cf4425f52d59ca5974d826cff80”}5.4 返回数据{ “sha”: “45c58a9358b67fc81e4034cb36c5196d791686ef”, “node_id”: “MDY6Q29tbWl0MTY0Nzk4NDczOjQ1YzU4YTkzNThiNjdmYzgxZTQwMzRjYjM2YzUxOTZkNzkxNjg2ZWY=”, “url”: “https://api.github.com/repos/ssshooter/test/git/commits/45c58a9358b67fc81e4034cb36c5196d791686ef", “html_url”: “https://github.com/ssshooter/test/commit/45c58a9358b67fc81e4034cb36c5196d791686ef", “author”: { “name”: “ssshooter”, “email”: “ssshooterx@gmail.com”, “date”: “2019-01-09T10:24:10Z” }, “committer”: { “name”: “ssshooter”, “email”: “ssshooterx@gmail.com”, “date”: “2019-01-09T10:24:10Z” }, “tree”: { “sha”: “2b3a65095ceb7cf4425f52d59ca5974d826cff80”, “url”: “https://api.github.com/repos/ssshooter/test/git/trees/2b3a65095ceb7cf4425f52d59ca5974d826cff80" }, “message”: “a Commit with GitHub api”, “parents”: [ { “sha”: “cda66de943082033f4b761639df77728d7bca4f0”, “url”: “https://api.github.com/repos/ssshooter/test/git/commits/cda66de943082033f4b761639df77728d7bca4f0”, “html_url”: “https://github.com/ssshooter/test/commit/cda66de943082033f4b761639df77728d7bca4f0” } ], “verification”: { “verified”: false, “reason”: “unsigned”, “signature”: null, “payload”: null }}6. 更新 Ref6.1 文档地址https://developer.github.com/v3/git/refs/#update-a-reference6.2 请求地址https://api.github.com/repos/ssshooter/test/git/refs/heads/master6.3 请求参数{ “sha”:“45c58a9358b67fc81e4034cb36c5196d791686ef”, “force”:true}6.4 返回数据{ “ref”: “refs/heads/master”, “node_id”: “MDM6UmVmMTY0Nzk4NDczOm1hc3Rlcg==”, “url”: “https://api.github.com/repos/ssshooter/test/git/refs/heads/master", “object”: { “sha”: “45c58a9358b67fc81e4034cb36c5196d791686ef”, “type”: “commit”, “url”: “https://api.github.com/repos/ssshooter/test/git/commits/45c58a9358b67fc81e4034cb36c5196d791686ef" }}7. node.js 实践代码var updateGitHubRes = function(blob, path) { var commitSha var commitTreeSha return getRef() .then(({ data }) => { commitSha = data.object.sha return getCommit(commitSha) }) .then(({ data }) => { commitTreeSha = data.tree.sha return createBlob(blob) }) .then(({ data }) => { var blobSha = data.sha return createTree(commitTreeSha, path, blobSha) }) .then(({ data }) => { var treeSha = data.sha return createCommit(commitSha, treeSha) }) .then(({ data }) => { var newCommitSha = data.sha return updataRef(newCommitSha) }) .catch(err => { console.log(err) })}var getRef = function() { return axios.get(/${owner}/${repo}/git/refs/heads/master)}var getCommit = function(commitSha) { return axios.get(/${owner}/${repo}/git/commits/${commitSha})}var createBlob = function(content) { return axios.post(/${owner}/${repo}/git/blobs, { content, encoding: ‘utf-8’ })}var createTree = function(base_tree, path, sha) { return axios.post(/${owner}/${repo}/git/trees, { base_tree, // commit tree 的 sha tree: [ { path, // 文件路径 mode: ‘100644’, // 类型,详情看文档 type: ‘blob’, sha // 刚才生成的 blob 的 sha } ] })}var createCommit = function(parentCommitSha, tree, message = ‘update post’) { return axios.post(/${owner}/${repo}/git/commits, { message, parents: [parentCommitSha],// 上次 commit 的sha tree })}var updataRef = function(newCommitSha) { return axios.post(/${owner}/${repo}/git/refs/heads/master, { sha: newCommitSha, force: true })}参考文献https://int128.hatenablog.com/entry/2017/09/05/161641https://juejin.im/post/5c33f49de51d45523070f7bbhttps://git-scm.com/book/zh/v2/Git-内部原理-Git-引用

January 15, 2019 · 3 min · jiezi

为自己搭建一个分布式 IM 系统二【从查找算法聊起】

前言最近这段时间确实有点忙,这篇的目录还是在飞机上敲出来了的。言归正传,上周更新了 cim 第一版;没想到反响热烈,最高时上了 GitHub Trending Java 版块的首位,一天收到了 300+ 的 star。现在总共也有 1.3K+ 的 star,有几十个朋友参加了测试,非常感谢大家的支持。在这过程中也收到一些 bug 反馈,feature 建议;因此这段时间我把一些影响较大的 bug 以及需求比较迫切的 feature 调整了,本次更新的 v1.0.1 版本:客户端超时自动下线。新增 AI 模式。聊天记录查询。在线用户前缀模糊匹配。下面谈下几个比较重点的功能。客户端超时自动下线 这个功能涉及到客户端和服务端的心跳设计,比较有意思,也踩了几个坑;所以准备留到下次单独来聊。AI 模式大家应该还记得这个之前刷爆朋友圈的 估值两个一个亿的 AI 核心代码。和我这里的场景再合适不过了。于是我新增了一个命令用于一键开启 AI 模式,使用情况大概如下。欢迎大家更新源码体验,融资的请私聊我????。聊天记录聊天记录也是一个比较迫切的功能。使用命令 :q 关键字 即可查询与个人相关的聊天记录。这个功能其实比较简单,只需要在消息发送及接收消息时保存即可。但要考虑的一点是,这个保存消息是 IO 操作,不可避免的会有耗时;需要尽量避免对消息发送、接收产生影响。异步写入消息因此我把消息写入的过程异步完成,可以不影响真正的业务。实现起来也挺简单,就是一个典型的生产者消费者模式。主线程收到消息之后直接写入队列,另外再有一个线程一直源源不断的从队列中取出数据后保存聊天记录。大概的代码如下:写入消息的同时会把消费消息的线程打开:而最终存放消息记录的策略,考虑后还是以最简单的方式存放在客户端,可以降低复杂度。简单来说就是根据当前日期+用户名写入到磁盘里。当客户端关闭时利用线程中断的方式停止了消费队列的线程。这点的设计其实和 logback 写日志的方式比较类似,感兴趣的可以去翻翻 logback 的源码,更加详细。回调接口至于收到其他客户端发来的消息时则是利用之前预留的消息回调接口来写入日志。收到消息后会执行自定义的回调接口。于是在这个回调方法中实现写入逻辑即可,当后续还有其他的消息处理逻辑时也能在这里直接添加。当处理逻辑增多时最好是改为责任链模式,更加清晰易维护。查找算法接下来是本文着重要讨论的一个查找算法,准确的说是一个前缀模糊匹配的算法。实现的效果如下:使用命令 :qu prefix 可以按照前缀的方式搜索用户信息。当然在命令行中其实意义不大,但是在移动端中确是比较有用的。类似于微信按照用户名匹配:因为后期打算出一个移动端 APP,所以就先把这个功能实现了。从效果也看得出来:就是按照输入的前缀匹配字符串(目前只支持英文)。在没有任何限制的条件下最快、最简单的实现方式可以直接把所有的字符串存放在一个容器中 (List、Set),查询时则挨个遍历;利用 String.startsWith(“prefix”) 进行匹配。但这样会有几个问题:存储资源比较浪费,不管是 list 还是 Set 都会有额外的损耗。查询效率较低,需要遍历集合后再遍历字符串的 char 数组(String.startsWith 的实现方式)。字典树基于以上的问题我们可以考虑下:假设我需要存放 java,javascript,jsp,php 这些字符串时在 ArrayList 中会怎么存放?很明显,会是这样完整的存放在一个数组中;同时这个数组还可能存在浪费,没有全部使用完。但其实仔细观察这些数据会发现有一些共同特点,比如 java,javascript 有共同的前缀 java;和 jsp 有共同的前缀 j。那是否可以把这些前缀利用起来呢?这样就可以少存储一份。比如写入 java,javascript 这两个字符串时存放的结构如下:当再存入一个 jsp 时:最后再存入 jsf 时:相信大家应该已经看明白了,按照这样的存储方式可以节省很多内存,同时查询效率也比较高。比如查询以 jav 开头的数据,只需要从头结点 j 开始往下查询,最后会查询到 ava 以及 script 这两个个结点,所以整个查询路径所经历的字符拼起来就是查询到的结果java+javascript。如果以 b 开头进行查询,那第一步就会直接返回,这样比在 list 中的效率高很多。但这个图还不完善,因为不知道查询到啥时候算是匹配到了一个之前写入的字符串。比如在上图中怎么知道 j+ava 是一个我们之前写入的 java 这个字符呢。因此我们需要对这种是一个完整字符串的数据打上一个标记:比如这样,我们将 ava、script、p、f 这几个节点都换一个颜色表示。表明查询到这个字符时就算是匹配到了一个结果。而查到 s 这个字符颜色不对,代表还需要继续往下查。比如输入关键字 js 进行匹配时,当它的查询路径走到 s 这里时判断到 s 的颜色不对,所以不会把 js 作为一个匹配结果。而是继续往下查,发现有两个子节点 p、f 颜色都正确,于是把查询的路径 jsp 和 jsf 都作为一个匹配结果。而只输入 j,则会把下面所有有色的字符拼起来作为结果集合。这其实就一个典型的字典树。具体实现下面则是具体的代码实现,其实算法不像是实现一个业务功能这样好用文字分析;具体还是看源码多调试就明白了。谈下几个重点的地方吧:字典树的节点实现,其中的 isEnd 相当于图中的上色。利用一个 Node[] children 来存放子节点。为了可以区分大小写查询,所以子节点的长度相当于是 26*2。写入数据这里以一个单测为例,写入了三个字符串,那最终形成的数据结构如下:图中有与上图有几点不同:每个节点都是一个字符,这样树的高度最高为52。每个节点的子节点都是长度为 52 的数组;所以可以利用数组的下标表示他代表的字符值。比如 0 就是大 A,26 则是小 a,以此类推。有点类似于之前提到的布隆过滤器,可以节省内存。debug 时也能看出符合上图的数据结构:所以真正的写入步骤如下:把字符串拆分为 char 数组,并判断大小写计算它所存放在数组中的位置 index。将当前节点的子节点数组的 index 处新增一个节点。如果是最后一个字符就将新增的节点置为最后一个节点,也就是上文的改变节点颜色。最后将当前节点指向下一个节点方便继续写入。查询总的来说要麻烦一些,其实就是对树进行深度遍历;最终的思想看图就能明白。所以在 cim 中进行模糊匹配时就用到了这个结构。字典树的源码在此处:https://github.com/crossoverJie/cim/blob/master/cim-common/src/main/java/com/crossoverjie/cim/common/data/construct/TrieTree.java其实利用这个结构还能实现判断某个前缀的单词是否在某堆数据里、某个前缀的单词出现的次数等。总结目前 cim 还在火热内测中(虽然群里只有20几人),感兴趣的朋友可以私聊我拉你入伙☺️ 再没有新的 BUG 产生前会着重把这些功能完成了,不出意外下周更新 cim 的心跳重连等机制。完整源码:https://github.com/crossoverJie/cim如果这篇对你有所帮助还请不吝转发。 ...

January 14, 2019 · 1 min · jiezi

Git 常用命令集

Git 常用命令集个人使用git的一些总结,一下常用命令的互相搭配使用,能非常完美的处理好日常遇到的99%的情况(不敢说100%,得留点余地)。 还有一些使用频率非常低的指令没有记录,至少普通开发者非常不常用,以后可能会补充。话不多说 首先需要先下载git这个还是要说一下的1. 常用命令1.1 最常用/版本对比/其他git status 查看当前分支状态 git reflog 查看每一次的命令,都做过什么 git log 查看此分支完整的提交记录,回车继续看,q停止 git log –oneline 查看此分支简略的提交记录,只展示提交号和提交信息 git show 查看最近一次提交的具体代码变化 git show <提交ID> 查看某次提交的具体代码变化 git diff 查看当前代码add后,会add哪些内容 git diff –staged 查看现在commit提交后,会提交哪些内容 git diff HEAD 查看当前代码add并提交后,会提交哪些内容 git log –oneline –graph 图形化展示合并历史1.2 初始化基本操作git init 初始化仓库,默认为mast分支 git add -A 提交全部文件修改到缓存区 git add <具体某个文件路径+全名> 提交某些文件到缓存区 git commit -m “<注释>” 提交代码到本地仓库,并写提交注释 git commit –a -m “<新注释>” 更改上次提交的注释1.3 分支操作git branch 查看本地所有分支 git branch -r 查看远程所有分支 git branch -a 查看本地和远程所有分支 git merge <分支名> 合并分支 git merge –abort 合并分支出现冲突时,取消合并,一切回到合并前的状态 git branch <新分支名> 基于当前分支,新建一个分支 git checkout –orphan <新分支名> 新建一个空分支(会保留之前分支的所有文件) git branch -D <分支名> 删除本地某个分支 git push <远程库名> :<分支名> 删除远程某个分支 git branch <新分支名称> <提交ID> 从提交历史恢复某个删掉的某个分支 git branch -m <原分支名> <新分支名> 分支更名 git checkout <分支名> 切换到本地某个分支 git checkout <远程库名>/<分支名> 切换到线上某个分支 git checkout -b <新分支名> 把基于当前分支新建分支,并切换为这个分支1.4 推拉操作(push和pull)git pull <远程仓库名> <远程分支名> 拉取远程仓库的分支与本地当前分支合并 git pull <远程仓库名> <远程分支名>:<本地分支名> 拉取远程仓库的分支与本地某个分支合并 git push <远程仓库名> <本地分支名> 推送本地某个分支到远程与其对应的分支上,远程没有则会给远程库新建分支 git push <远程仓库名> <本地分支名>:<远程分支名> 推送本地分支到远程某个分支 git push <远程仓库名> :<远程分支名> 删除远程某个分支 git push -f <同上的命令> 强制提交,覆盖线上 git fetch 获取线上最新版信息记录,不合并,用法和pull类似(一个特殊的操作,比如多人开发分支,其他人提交后,自己看分支的时候,可能还知不道线上的分支已经比自己的新了,需要这个指令,来获取一下线上的最新版本,会拉取,但不会合并,存在本地仓库中)1.5 查看具体文件版本区别(git diff)git diff <文件名> 对比最近提交,和近近次提交的区别,不加文件名,则为整体对比 git diff HEAD^ – <文件名> 同上 git diff HEAD~<一个数字> – <文件名> 上次提交和前第几次提交作对比 git diff <提交ID> <文件名> 上次提交和某次提交作对比 git diff <文件名> 工作区与暂存区比较 git diff HEAD <文件名> 工作区与最近提交比较 git diff <本地分支名> <文件名> 当前分支的文件与其他分支的文件进行比较1.6 回滚操作注意,当回滚代码时,reset和revert都可以使用,下面是两个指令的区别 reset:真实硬性回滚,目标版本后面的提交记录全部丢失了 revert:同样回滚,但实际这个回滚操作,算是一个提交,目标版本后面的提交记录也全部都有,而且会多一次提交,就是这次revert–hard的功能:不加他,文件修改会保留,都会处于add之前的状态;加上他,文件修改会被删除,丢失掉git <reset/revert> <–hard?> HEAD^ 回退到上次提交 git <reset/revert> <–hard?> <提交记录> 回退到之前的某次提交 git <reset/revert> <–hard?> <某个分支> 回退到此分支的提交状态,相当于复制分支过来 git reset <文件完整路径+完整名> 把add过的某个文件撤销到未add的状态 git reset 把add过的所有文件撤销到未add的状态 git checkout <文件完整路径+完整名> 一个还没有add的文件,撤销修改 git checkout . 还没有add的所有文件,撤销修改1.7 远程库和分支的链接操作git remote -v 查看对应的远程仓库地址 git clone <远程仓库地址链接> 克隆某个远程库,默认库名为origin git clone <远程仓库地址链接> <库名> 克隆某个远程库,并自定库名 git remote remove <远程仓库名> 和远程的某个仓库解除绑定关系 git remote add <远程仓库名> <远程仓库地址链接> 和某个远程仓库建立绑定关系 git push –set-upstream <远程仓库名> <远程分支名> 当前分支和远程某个分支建立绑定关系1.8 储藏操作(stash)经常有这样的事情发生,当你正在进行项目中某一部分的工作,里面的东西处于一个比较杂乱的状态。 而你想转到其他分支上进行一些工作。 问题是,你不想提交进行了一半的工作,否则以后你无法回到这个工作点。 解决这个问题的办法就是git stash命令。git stash save <注释信息> 当前分支提交到储藏,插到储藏序列的最前面(不包括新建文件) git stash save -u <注释信息> 同上,会包括新建文件,功能更强大 git stash list 查看所有储藏中的工作 git stash drop <储藏的名称> 删除对应的某个储藏 git stash pop 取出储藏中最后存入的工作状态进行恢复,会删除储藏 git stash pop <储藏对应的数字> 取出储藏中对应的工作状态进行恢复,会删除储藏 git stash apply 取出储藏中最后存入的工作状态进行恢复,注意,不会删除储藏 git stash apply <储藏的名称> 取出储藏中对应的工作状态进行恢复,注意,不会删除储藏 git stash branch <新分支名> <储藏的名称> 从储藏中新建分支,会删除储藏 git stash clear 清空所有储藏中的工作1.9 标签操作(tag)tag 是什么,虽然整个开发过程,每个提交都对应一个提交ID,回滚也比较方便,但总有一些重大意义的提交,比如从v0.91升级到了v1.0,这个v1.0是必须要单独摘出来保存的,否则万一出现回滚事故,导致提交记录丢失呢? 虽然可以单独新建分支保存,但分支的功能并不是用来做这个的。冗余的分支在开发时也容易产生误操作。所以tag的用处就来了,他可以把某次提交后的项目,整个单独复制拎出来,命名为一个tag(可以以项目版本或打tag的时间来命名),可以写注释,存在tag列表中,与分支隔离开,除非专门命令操作,否则只要项目存在,他们就不会受到影响。同时,可以使用tag快速回滚项目、基于tag新建分支等,在项目整体的管理上非常方便。git tag 列出本仓库中所有tag列表 git tag <tag名>’ 基于最近的一次提交打tag(轻量tag,不推荐) git tag -a <tag名> -m ‘<此tag的注释>’ 基于最近的一次提交打tag git tag -a <tag名> <提交ID> -m ‘<此tag的注释>’ 基于某次提交打tag git show <tag名> 查看此tag的tag信息 git tag -d <tag名> 删除本地某个tag git push origin :refs/tags/<tag名> 删除远程仓库中的tag git push <远程仓库名> <tag名>’ 推送某个tag至某个远程库 git push <远程仓库名> –tags’ 推送所有tag至某个远程库 git checkout -b <新分支名> <tag名>’ 基于某个tag新建分支2. .gitignore文件在使用Git的过程中,有的文件比如日志、临时文件、编译的中间文件等不要提交到代码仓库,这时就要设置相应的忽略规则,来忽略这些文件的提交。没错就是.gitignore文件了。此文件需要放在.git(默认是隐藏文件,是git的本地仓库)同一目录下写法如下:node_modules/dist/.mapnpm-debug.logyarn-debug.logyarn-error.log这样,当git add的时候,上面定义忽略的那些文件,就会被git忽略,发生了变化git也不会管。然而有时候,是会出现意外的。比如,git已经提交过了,这是后突然说,这里面有个文件需要被忽略,然后我把这个文件写到了.gitignore文件中。。。无效!这个文件仍然没有被忽略!这是就需要如下命令,来删除此文件并忽略git rm -r –cached <文件路径+文件全名> 在git库中删除文件并停止追踪,但实际文件还保留,只是忽略掉。由此,方可。3. 生成SSH公钥绑定一个远程仓库,有两种方式:使用http链接的方式使用ssh加密链接的方式如果远程仓库为公开仓库,则两者在使用上,区别不大; 但如果是私密仓库,则http方式,需要输入用户名和密码登录后,才可建立联系,但这样一来,需要把私密仓库的账号和密码暴露,即使添加了项目团队成员,如果成员的账号密码暴露,任何人在任何设备上,登录账号密码,至少都能对项目进行拉取操作,所以就有了下面更合适的方式—ssh。ssh的话,则只需要本地设备,输入一条git指令,生成一对公钥和私钥,然后把公钥的内容,复制添加到远程库的设置中,让远程库认识此设备,就想当于用户填写了账号和密码,好处是,这种方法在远程库的添加上,只认设备不认账户,这样只用保证设备是安全的,仓库就是安全的。下面为本地设备生成ssh输入ssh-keygen来生成ssh window默认存放在C盘/用户/<用户名称>/.ssh文件夹中 Mac默认存放在硬盘/用户/<用户名称>/.ssh文件夹中 其中两个文件,id_rsa.pub为公钥,需要打开复制其中的内容,粘贴到需要的网站中(另一个为私钥,切记保管好)4. 设置用户名、邮箱、登录账户git config user.name 查看设置的用户名 git config user.email 查看设置的用户邮箱 git config –global user.name <用户名> 设置用户名 git config –global user.email <邮箱> 设置用户邮箱 修改用户登录信息window控制面板 -> 用户账户 -> 管理 Windows 凭据,即可看到普通凭据中有git的账户信息,可编辑更改Mac钥匙串访问.app -> 右上角搜索git,即可看到,可编辑更改 ...

January 14, 2019 · 2 min · jiezi

git解决pre -commit hook failed (add --no-verify to bypass)的问题

在同步本地项目到github是出现这个错误:pre -commit hook failed (add –no-verify to bypass)pre-commit钩子惹的祸当你在终端输入git commit -m “xxx”,提交代码的时候,pre-commit(客户端)钩子,它会在Git键入提交信息前运行做代码风格检查。如果代码不符合相应规则,则报错。2. ‘弱鸡’的解决方案为了省点事,我并没有花功夫去查找代码哪里不符合规范.我是暴力地把pre-commit钩子给删除了!具体步骤:进入项目的.git文件夹(文件夹默认隐藏,可先设置显示或者命令ls查找)再进入hooks文件夹删除pre-commit文件重新git commit -m ‘xxx’ git push即可。SourceTree也可以正常推送总结一句话: 删除.git的提交规则文件: rm -rf ./git/hooks/pre-commit文章转自:https://www.cnblogs.com/soyxi…

January 13, 2019 · 1 min · jiezi

最小化 Java 镜像的常用技巧

背景随着容器技术的普及,越来越多的应用被容器化。人们使用容器的频率越来越高,但常常忽略一个基本但又非常重要的问题 - 容器镜像的体积。本文将介绍精简容器镜像的必要性并以基于 spring boot 的 java 应用为例描述最小化容器镜像的常用技巧。精简容器镜像的必要性精简容器镜像是非常必要的,下面分别从安全性和敏捷性两个角度进行阐释。安全性基于安全方面的考虑,将不必要的组件从镜像中移除可以减少攻击面、降低安全风险。虽然 docker 支持用户通过 Seccomp 限制容器内可以执行操作或者使用 AppArmor 为容器配置安全策略,但它们的使用门槛较高,要求用户具备安全领域的专业素养。敏捷性精简的容器镜像能提高容器的部署速度。假设某一时刻访问流量激增,您需要通过增加容器副本数以应对突发压力。如果某些宿主机不包含目标镜像,需要先拉取镜像,然后启动容器,这时使用体积较小的镜像能加速这一过程、缩短扩容时间。另外,镜像体积越小,其构建速度也越快,同时还能减少存储和传输的成本。常用技巧将一个 java 应用容器化所需的步骤可归纳如下:编译 java 源码并生成 jar 包。将应用 jar 包和依赖的第三方 jar 包移动到合适的位置。本章所用的样例是一个基于 spring boot 的 java 应用 spring-boot-docker,所用的未经优化的 dockerfile 如下:FROM maven:3.5-jdk-8COPY src /usr/src/app/srcCOPY pom.xml /usr/src/appRUN mvn -f /usr/src/app/pom.xml clean packageENTRYPOINT [“java”,"-jar","/usr/src/app/target/spring-boot-docker-1.0.0.jar"]由于应用使用 maven 构建,dockerfile 中指定maven:3.5-jdk-8作为基础镜像,该镜像的大小为 635MB。通过这种方式最终构建出的镜像非常大,达到了 719MB,这是因为一方面基础镜像本身就很大,另一方面 maven 在构建过程中会下载许多用于执行构建任务的 jar 包。多阶段构建Java 程序的运行只依赖 JRE,并不需要 maven 或者 JDK 中众多用于编译、调试、运行的工具,因此一个明显的优化方法是将用于编译构建 java 源码的镜像和用于运行 java 应用的镜像分开。为了达到这一目的,在 docker 17.05 版本之前需要用户维护 2 个 dockerfile 文件,这无疑增加了构建的复杂性。好在自 17.05 开始,docker 引入了多阶段构建的概念,它允许用户在一个 dockerfile 中使用多个 From 语句。每个 From 语句可以指定不同的基础镜像并将开启一个全新的构建流程。您可以选择性地将前一阶段的构建产物复制到另一个阶段,从而只将必要的内容保留在最终的镜像里。优化后的 dockerfile 如下:FROM maven:3.5-jdk-8 AS buildCOPY src /usr/src/app/srcCOPY pom.xml /usr/src/appRUN mvn -f /usr/src/app/pom.xml clean packageFROM openjdk:8-jreARG DEPENDENCY=/usr/src/app/target/dependencyCOPY –from=build ${DEPENDENCY}/BOOT-INF/lib /app/libCOPY –from=build ${DEPENDENCY}/META-INF /app/META-INFCOPY –from=build ${DEPENDENCY}/BOOT-INF/classes /appENTRYPOINT [“java”,"-cp",“app:app/lib/”,“hello.Application”]该 dockerfile 选用maven:3.5-jdk-8作为第一阶段的构建镜像,选用openjdk:8-jre作为运行 java 应用的基础镜像并且只拷贝了第一阶段编译好的.claass文件和依赖的第三方 jar 包到最终的镜像里。通过这种方式优化后的镜像大小为 459MB。使用 distroless 作为基础镜像虽然通过多阶段构建能减小最终生成的镜像的大小,但 459MB 的体积仍相对过大。经调查发现,这是因为使用的基础镜像openjdk:8-jre体积过大,到达了 443MB,因此下一步的优化方向是减小基础镜像的体积。Google 开源的项目 distroless 正是为了解决基础镜像体积过大这一问题。Distroless 镜像只包含应用程序及其运行时依赖项,不包含包管理器、shell 以及在标准 Linux 发行版中可以找到的任何其他程序。目前,distroless 为依赖 java、python、nodejs、dotnet 等环境的应用提供了基础镜像。使用 distroless 的 dockerfile 如下:FROM maven:3.5-jdk-8 AS buildCOPY src /usr/src/app/srcCOPY pom.xml /usr/src/appRUN mvn -f /usr/src/app/pom.xml clean packageFROM gcr.io/distroless/javaARG DEPENDENCY=/usr/src/app/target/dependencyCOPY –from=build ${DEPENDENCY}/BOOT-INF/lib /app/libCOPY –from=build ${DEPENDENCY}/META-INF /app/META-INFCOPY –from=build ${DEPENDENCY}/BOOT-INF/classes /appENTRYPOINT [“java”,"-cp",“app:app/lib/”,“hello.Application”]该 dockerfile 和上一版的唯一区别在于将运行阶段依赖的基础镜像由openjdk:8-jre(443 MB)替换成了gcr.io/distroless/java(119 MB)。经过这一优化,最终镜像的大小为 135MB。使用 distroless 的唯一不便是您无法 attach 到一个正在运行的容器上排查问题,因为镜像中不包含 shell。虽然 distroless 的 debug 镜像提供 busybox shell,但需要用户重新打包镜像、部署容器,对于那些已经基于非 debug 镜像部署的容器无济于事。 但从安全角度来看,无法 attach 容器并不完全是坏事,因为攻击者无法通过 shell 进行攻击。使用 alpine 作为基础镜像如果您确实有 attach 容器的需求,又希望最小化镜像的大小,可以选用 alpine 作为基础镜像。Alpine 镜像的特点是体积非常下,基础款镜像的体积仅 4 MB 左右。使用 alpine 后的 dockerfile 如下:FROM maven:3.5-jdk-8 AS buildCOPY src /usr/src/app/srcCOPY pom.xml /usr/src/appRUN mvn -f /usr/src/app/pom.xml clean packageFROM openjdk:8-jre-alpineARG DEPENDENCY=/usr/src/app/target/dependencyCOPY –from=build ${DEPENDENCY}/BOOT-INF/lib /app/libCOPY –from=build ${DEPENDENCY}/META-INF /app/META-INFCOPY –from=build ${DEPENDENCY}/BOOT-INF/classes /appENTRYPOINT [“java”,"-cp",“app:app/lib/*”,“hello.Application”]这里并未直接继承基础款 alpine,而是选用从 alpine 构建出的包含 java 运行时的openjdk:8-jre-alpine(83MB)作为基础镜像。使用该 dockerfile 构建出的镜像体积为 99.2MB,比基于 distroless 的还要小。执行命令docker exec -ti <container_id> sh可以成功 attach 到运行的容器中。distroless vs alpine既然 distroless 和 alpine 都能提供非常小的基础镜像,那么在生产环境中到底应该选择哪一种呢?如果安全性是您的首要考虑因素,建议选用 distroless,因为它唯一可运行的二进制文件就是您打包的应用;如果您更关注镜像的体积,可以选用 alpine。其他技巧除了可以通过上述技巧精简镜像外,还有以下方式:将 dockerfile 中的多条指令合并成一条,通过减少镜像层数的方式达到精简镜像体积的目的。将稳定且体积较大的内容置于镜像下层,将变动频繁且体积较小的内容置于镜像上层。虽然该方式无法直接精简镜像体积,但充分利用了镜像的缓存机制,同样可以达到加快镜像构建和容器部署的目的。想了解更多优化 dockerfile 的小窍门可参考教程 Best practices for writing Dockerfiles。总结本文通过一系列的优化,将 java 应用的镜像体积由最初的 719MB 缩小到 100MB 左右。如果您的应用依赖其他环境,也可以用类似的原则进行优化。针对 java 镜像,google 提供的另一款工具 jib 能为您屏蔽镜像构建过程中的复杂细节,自动构建出精简的 java 镜像。使用它您无须编写 dockerfile,甚至不需要安装 docker。对于类似 distroless 这样无法 attach 或者不方便 attach 的容器,建议您将它们的日志中心化存储,以便问题的追踪和排查。具体方法可参考文章面向容器日志的技术实践。本文作者:吴波bruce_wu阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

January 11, 2019 · 2 min · jiezi

聊聊技术写作的个人体会

有群友问过,是什么原因使我开始写技术公众号,又是什么动力让我坚持写的。在我看来,写作是一件不能敷衍的事,通过写作来学习,反而要比单纯地学习的效果要好。为了写成一篇“拿得出手”的文章,我要反复查找资料,阅读与思考,拆解与整合,最终写成的时候,也是知识的拼图成型的时候。所以,对我来说,写作是一种咀嚼信息而后提炼知识,最终拓展成技能与认知的过程。虽然这个过程很缓慢,但曾经的急进方式并没有速成的效果啊,不妨就这样一文章一脚印地试试看咯。除此之外,还有一个很重要的原因。文章是一种公共对话的媒介,它是一个展示的窗口,也是一个接收反馈的通道。通过写作,我有了跟其它学习者对话的机会。看书学习可能只是个人的事情,但是,在写作平台上发布文章,这就超越了个人行为——你得随时准备着被批评、或者被请教、或者被误解、甚至是被无视(这是最常见的结果)。我享受写作文章,来跟其他处在相同处境的同学们交流,来向更优秀的大牛们学习取经。这就是我目前写技术文章的一些个人体会吧。对于上面提到的第二个原因,我最近颇有感触,想要多聊一些。为了更有针对性,本文姑且限定一个话题吧,那就是“写作技术文章,如何看待他人的批评/意见”。1、主观性的意见有些声音其实只是主观看法,我认为可以和而不同。主观世界往往没有确切的对错之分,毕竟——思想无罪 。面对主观性的意见,我认为要做到有理有据,坚持一点个性,最后会得到别人的尊重。比如,在翻译 Python 社区的七种治理模式的时候,有一个提案是“Python Governance Model Lead by Trio of Pythonistas”,我将它翻译成“三巨头治理模式”。有同学就指出,“Trio”应该翻译成“三人组”或者“三重奏”,翻译成“三巨头”是什么意思?这种留言,我认为是主观性的意见,应求同存异。我之所以这么翻译,一方面考虑,它要替代的是“终身仁慈独裁者”,三巨头对独裁者,意味深长;另一方面,我脑子里总想着一个皇帝死了,然后政权被三个摄政大臣把持,这种政治画面挥之不去,虽然是不着边际,但挺有趣味,所以我不肯放弃这“三巨头”的译法。主观性的意见带入了提出者的个人知识背景、思想结构、以及话语习惯等等,我觉得要先尝试交流,相互交换,能融洽兼容则最好啦,不能的话,及时终止。2、客观性的意见客观性的意见有如下几种:笔误(错别字和其它疏忽)、代码规范、知识性错误……对于笔误性的错误,这没啥好说的,我自己发现过几处,也被读者指出过几处。有则改之就好。对于代码规范,有时候为了举例方便,确实没有按照规范来。尽量避免,求一个兼顾。知识性错误是要热烈欢迎的——不是说欢迎错误,而是说欢迎别人来指出我所未知的错误。 出现知识性的错误,就意味着没有全面掌握知识,一旦出现,就必然意味着有提升的空间。本来以为知道了什么,如果被指出了错误,那改正后,才是真的知道了什么。知道自己不知道并且改正之,并不可耻,不知道自己不知道,这才可怜。 在写《Python是否支持复制字符串呢?》的时候,我根据已得的知识,以及查阅到的资料,早早就得出了一个很满意的结论。最后成文前,临时地加了一个未作验证的示例,没想到这会是一个致命的反例,推翻了前面辛辛苦苦建立起来的一切。这是一个客观性的错误,一被指出的时候,很快就能验证。因为这个错误,我重新梳理了相关的知识点,组成新的知识面,写成了一篇《join()方法的神奇用处与Intern机制的软肋》。还有一个例子,前不久的《Python进阶:自定义对象实现切片功能》,我在准备素材的时候,竟采用了一个不严谨的例子,而且自作聪明地批判了别人的实际无误的例子。最后,有读者留言了很长的不同观点,我才意识到自己的错误!得益于读者的留言,我修正了自己的错误,而且在修正过程中,也加强了对于其它知识的理解,真是塞翁失马焉知非福啊。3、内置函数与内置类这里还有一个客观性错误,藏得特别深,可能真的有 90% 的 Python 使用者不知道。 特别感谢 @xpresslink 同学指出。下面,我给大家分享一下。在文章《为什么range不是迭代器?range到底是什么类型?》里,我的注意点其实就在标题的两个问句里,大部分的留言互动也是基于此。但最后,很意外地,一名读者指出了一个客观性错误,让我有了额外的收获。这位同学指出我有些基本的概念是错误的:“range() 函数”这个说法是非常明显有错误的,range 不是内置函数( builtin method )而是个类对象,在 python 里面不要见到用括号调用的东西就认为是函数,类似的还是有很多,如 list, set, tuple, dict 等,这些都是类, 特别是 enumerate ,这个学 python 的人十有八九认为是函数而不知道是类,加了括号是实例化而不是函数调用。python 中类的实例化和函数调用非常容易对新手有大的迷惑性,相对来说在 java 中有明确的 new 关键字加在构造方法前面概念更清楚一些。根据这个评论,我就去查看文档。上图中 range() 虽然被归类到 Built-in Functions 里面,但是官方描述的是“functions and types”,即是说,在内置函数的大类下面,包含了内置函数与内置类。那 range() 属于哪一种呢?看看它的解释:Rather than being a function, range is actually an immutable sequence type…… range 实际是一种不可变的序列类型,而非一个(内置)函数……按照这里的说法,官方已经区分了 range() 不是函数,正像那位留言的同学所说。我第一反应当然是不能接受。我怎么会认为它是内置函数的呢,难道不是根据学习资料得来的么?难道我学习的资料是错的?为何从来没看到有人对此做过辨析呢?根据群友的提示,我去查看 Python2 的文档,然后就发现了很有意思的地方:首先一点, Built-in Functions 的描述跟 Python3 有点不同,它写的是 “functions”,并不包含“types”;还有一点,在 range() 和 xrange() 的具体内容中,官方都是称呼它们为 function 。由此看来,Python2 的官方文档就把 range() 当成内置函数,这个认识错误是有根源的!等到 Python3 的时候,官方把错误改正过来了,然而改得并不彻底。才有了前面同时存在“functions and types”的描述。官方已经把 range() 与 xrange() 规范为一个,或许在今后版本,还会专门分出一类 Built-in Types 来存放像 range() 和 enumerate() 这些内置类吧。在那之前,我只能先行给大家提个醒了:别再误以为 range() 是内置函数了。 那么,怎么辨别哪些是内置函数呢?我想到了两个方法:(1)看是否存在对应的魔术方法。例如,len() 是一个内置函数,因为它实际调用的是魔术方法__len__() ;还有最近一直在提的 iter(),它调用的是__iter__() ,所以也是内置函数;而因为不存在 range() 魔术方法,所以 range() 不是内置函数。(2)使用 type() 进行判断,结果为 builtin_function_or_method 的才是内置函数。>>> type(len)builtin_function_or_method>>> type(sorted)builtin_function_or_method>>> type(open)builtin_function_or_method>>> type(range)type>>> type(enumerate)type>>> type(str)type像 open 和 sorted 并没有对应的魔术方法,但判断出来都是内置函数;而 str 虽有对应魔术方法,但判断是 type ,这意味着,以上两种方法得要结合起来看。我不确定有多少人事先知道怎么区分内置函数与内置类,但我确实没看到过对这个问题进行辨析的文章,所以,这次是真正涨知识了,也希望这篇文章,能够消除一些读者的错误观念吧。4、小结我最近写的一些文章都不是心血来潮,不管是字符串系列、切片系列还是迭代器系列,本意都是想在一个主题上进行深入的多面性的思考与记录。如果没有一些热心读者的指正,我恐怕是很难知道自己错在了哪里,如果不是有这么多的认同以及意见,我恐怕也缺乏动力坚持写下去。最后鸣谢几位提意见的小能手同学(时间顺序,可能有漏):@疯琴、@德玛西亚之翼奎因、@发条橙、@gaieepo、@郭芮、@aijam、@xpresslink、@进击的团子、@不换……相关链接(单有错,双修正): 1、Python是否支持复制字符串呢?2、join()方法的神奇用处与Intern机制的软肋3、Python进阶:自定义对象实现切片功能4、Python进阶:全面解读高级特性之切片!5、为什么range不是迭代器?range到底是什么类型?—————–本文原创并首发于微信公众号【Python猫】,后台回复“爱学习”,免费获得20+本精选电子书。 ...

January 11, 2019 · 1 min · jiezi

java 日志脱敏框架 sensitive-新版本0.0.2-深度拷贝,属性为对象和集合的支持

项目介绍日志脱敏是常见的安全需求。普通的基于工具类方法的方式,对代码的入侵性太强。编写起来又特别麻烦。本项目提供基于注解的方式,并且内置了常见的脱敏方式,便于开发。用户也可以基于自己的实际需要,自定义注解。特性基于注解的日志脱敏可以自定义策略实现,策略生效条件常见的脱敏内置方案java 深拷贝,且原始对象不用实现任何接口。快速开始maven 导入<dependency> <groupId>com.github.houbb</groupId> <artifactId>sensitive</artifactId> <version>0.0.2</version></dependency>定义对象User.java我们对 password 使用脱敏,指定脱敏策略为 StrategyPassword。(直接返回 null)public class User { @Sensitive(strategy = StrategyChineseName.class) private String username; @Sensitive(strategy = StrategyCardId.class) private String idCard; @Sensitive(strategy = StrategyPassword.class) private String password; @Sensitive(strategy = StrategyEmail.class) private String email; @Sensitive(strategy = StrategyPhone.class) private String phone; //Getter & Setter //toString()}属性为集合或者对象如果某个属性是单个集合或者对象,则需要使用注解 @SensitiveEntry。放在集合属性上,且属性为普通对象会遍历每一个属性,执行上面的脱敏策略。放在对象属性上会处理对象中各个字段上的脱敏注解信息。放在集合属性上,且属性为对象遍历每一个对象,处理对象中各个字段上的脱敏注解信息。放在集合属性上,且属性为普通对象UserEntryBaseType.java作为演示,集合中为普通的字符串。public class UserEntryBaseType { @SensitiveEntry @Sensitive(strategy = StrategyChineseName.class) private List<String> chineseNameList; @SensitiveEntry @Sensitive(strategy = StrategyChineseName.class) private String[] chineseNameArray; //Getter & Setter & toString()}构建对象/** * 构建用户-属性为列表,列表中为基础属性 * @return 构建嵌套信息 * @since 0.0.2 /public static UserEntryBaseType buildUserEntryBaseType() { UserEntryBaseType userEntryBaseType = new UserEntryBaseType(); userEntryBaseType.setChineseNameList(Arrays.asList(“盘古”, “女娲”, “伏羲”)); userEntryBaseType.setChineseNameArray(new String[]{“盘古”, “女娲”, “伏羲”}); return userEntryBaseType;}测试演示/* * 用户属性中有集合或者map,集合中属性是基础类型-脱敏测试 * @since 0.0.2 /@Testpublic void sensitiveEntryBaseTypeTest() { UserEntryBaseType userEntryBaseType = DataPrepareTest.buildUserEntryBaseType(); System.out.println(“脱敏前原始: " + userEntryBaseType); UserEntryBaseType sensitive = SensitiveUtil.desCopy(userEntryBaseType); System.out.println(“脱敏对象: " + sensitive); System.out.println(“脱敏后原始: " + userEntryBaseType);}日志信息脱敏前原始: UserEntryBaseType{chineseNameList=[盘古, 女娲, 伏羲], chineseNameArray=[盘古, 女娲, 伏羲]}脱敏对象: UserEntryBaseType{chineseNameList=[古, 娲, 羲], chineseNameArray=[古, 娲, 羲]}脱敏后原始: UserEntryBaseType{chineseNameList=[盘古, 女娲, 伏羲], chineseNameArray=[盘古, 女娲, 伏羲]}放在对象属性上演示对象这里的 User 和上面的 User 对象一致。public class UserEntryObject { @SensitiveEntry private User user; @SensitiveEntry private List<User> userList; @SensitiveEntry private User[] userArray; //…}对象构建/ * 构建用户-属性为列表,数组。列表中为对象。 * @return 构建嵌套信息 * @since 0.0.2 /public static UserEntryObject buildUserEntryObject() { UserEntryObject userEntryObject = new UserEntryObject(); User user = buildUser(); User user2 = buildUser(); User user3 = buildUser(); userEntryObject.setUser(user); userEntryObject.setUserList(Arrays.asList(user2)); userEntryObject.setUserArray(new User[]{user3}); return userEntryObject;}测试演示/ * 用户属性中有集合或者对象,集合中属性是对象-脱敏测试 * @since 0.0.2 /@Testpublic void sensitiveEntryObjectTest() { UserEntryObject userEntryObject = DataPrepareTest.buildUserEntryObject(); System.out.println(“脱敏前原始: " + userEntryObject); UserEntryObject sensitiveUserEntryObject = SensitiveUtil.desCopy(userEntryObject); System.out.println(“脱敏对象: " + sensitiveUserEntryObject); System.out.println(“脱敏后原始: " + userEntryObject);}测试结果脱敏前原始: UserEntryObject{user=User{username=‘脱敏君’, idCard=‘123456190001011234’, password=‘1234567’, email='12345@qq.com’, phone=‘18888888888’}, userList=[User{username=‘脱敏君’, idCard=‘123456190001011234’, password=‘1234567’, email=‘12345@qq.com’, phone=‘18888888888’}], userArray=[User{username=‘脱敏君’, idCard=‘123456190001011234’, password=‘1234567’, email=‘12345@qq.com’, phone=‘18888888888’}]}脱敏对象: UserEntryObject{user=User{username=‘脱君’, idCard=‘12345634’, password=‘null’, email='123**@qq.com’, phone=‘1888888’}, userList=[User{username=‘脱君’, idCard=‘12345634’, password=‘null’, email=‘123**@qq.com’, phone=‘1888888’}], userArray=[User{username=‘脱君’, idCard=‘123456**34’, password=‘null’, email=‘123**@qq.com’, phone=‘1888888’}]}脱敏后原始: UserEntryObject{user=User{username=‘脱敏君’, idCard=‘123456190001011234’, password=‘1234567’, email='12345@qq.com’, phone=‘18888888888’}, userList=[User{username=‘脱敏君’, idCard=‘123456190001011234’, password=‘1234567’, email=‘12345@qq.com’, phone=‘18888888888’}], userArray=[User{username=‘脱敏君’, idCard=‘123456190001011234’, password=‘1234567’, email=‘12345@qq.com’, phone=‘18888888888’}]}需求 & BUGSissues欢迎加入开发如果你对本项目有兴趣,并且对代码有一定追求,可以申请加入本项目开发。如果你善于写文档,或者愿意补全测试案例,也非常欢迎加入。 ...

January 11, 2019 · 2 min · jiezi

三点助力Zilliqa主网上线安全无忧

作者: Vincent https://weibo.com/u/620460972...Zilliqa旨在利用分片技术解决区块链的扩展性难题,使得区块链更快、更具扩展性。尽管项目成立仅一年多时间,但已给我们带来太多惊喜。Zilliqa作为被业界寄予厚望的几个项目之一,进度也在不断更新,作为主网上线前的重要里程碑,测试网3.0“猫山王”已经发布。在以太坊苦苦困于低吞吐率之际,Zilliqa测试网已经取得了2800tps的成绩,许多人都认为Zilliqa可能会成为以太坊的强有力的竞争者。然而主网的发布也不是一蹴而就的,中间会遇到许多的挑战。因为想给社区带来一个有着智能合约和更多Dapp的主网,Zilliqa曾将主网上线时间推迟了一个季度。尽管有些延误,Zilliqa还是处于业内领先的地位,11.30日发布了全球首个集网络、交易、智能合约分片的测试网。有三个特性将助力Zilliqa上线后迅速走上正轨。分片技术分片技术类似于大数据处理中的云计算集群,通过将问题分而治之,大而化之来解决区块链的拓展性难题。与很多采用其他技术的公链不同,在分片技术中节点越多,网络的处理能力越强,并且在一定范围内处理能力是线性增加的,是目前解决安全性、拓展性、去中心化不可能三角最有效的方式。智能合约安全性Zilliqa的智能合约是其亮点之一,因为团队创始人都是网络安全出身,对区块链安全问题也有很深入的研究,看到很多现有公链在合约安全领域的欠缺,所以才会有新创一个智能合约语言的想法,通过形式化证明消除智能合约的潜在漏洞。形式化验证是一种基于数学和逻辑学的方法。具体来讲,在智能合约部署之前,对代码进行形式化建模,然后通过数学的手段对代码的安全性和功能正确性进行严格的证明,可有效检测出智能合约是否存在安全漏洞和逻辑漏洞。该方法可以有效弥补传统的靠人工经验查找代码逻辑漏洞的缺陷。形式化验证技术的优势在于,用传统的测试等手段无法穷举所有可能输入,而用数学证明的角度,就能克服这一问题。历经三次公测近期,Zilliqa发布第三版测试网“猫山王”,该测试网是集成交易分片、网络分片、智能合约分片等主网全部功能的完全成熟的测试网。在测试网2.0中,Zilliqa团队使用1000个节点、四个分片的配置达到了压测10k tps的成绩,测试网3.0中,团队配备了1200个节点,三个分片,能达到多少tps呢?让我们拭目以待!

January 10, 2019 · 1 min · jiezi

Scilla如何加速区块链交易

来自Zilliqa的Ilya Sergey和Amrit Kumar对TechRepublic的Dan Patterson表示,Zilliqa的编程语言非常独特,可以提供“语言不同维度之间的隔离”。更多采访信息请看下文。原视频地址:https://www.youtube.com/watch…Dan Patterson:请給我们详细地说说Zilliqa代码是如何工作的。Amrit Kumar:首先,Zilliqa的一个目标是要保证有高的吞度量。吞吐量可以大体上随着网络规模的扩大而线性增长。Zilliqa的另一个目标是拥有可以安全使用并安全部署的智能合约语言,如果你写的智能合约语言可以部署在链上,那就说明它是没有代码缺陷的,你可以证明合约的强大属性。例如,你可以证明你的合约不会冻结资金。你的合约不会将资金泄漏给其他人,比方说团体的资金,这就是Zilliqa的用武之地,Ilya将非常乐意详细说明这一点。Ilya Sergey:好的,让我从这里开始吧。我们将Zilliqa设计为一个编程语言,可以用来编写人们认为适合智能合约领域的典型应用,例如ICO,不同的众筹项目,游戏,但它是一种非常通用的语言。SEE:IT Hiring Kit: 程序员(Tech Pro Research)因此,它与其他智能合约编程语言(例如Solidity)的不同之处在于语言的不同维度之间的隔离。这种隔离使得在Zilliqa上编写的合约更容易在分片上执行,并验证合约实际上正在做他们应该做的事情。从这种角度来说,合约实际上包含了多种功能,它们充当相互通信的代理,也充当区块链用户转移资金的代理,它们还可以充当执行计算任务的普通程序。计算和通信这两个方面是非常正交的,可以对它们分别进行讨论。合约之间的交互是通过把通信作为合约的外部接口来实现的,使得智能合约之间的交互能够适应Zilliqa的这种分片架构,Amrit将会对此加以阐述。每当合约与另一个合约进行交互时,都只由系统中的一部分进行处理,如一个分片,或分片的子集,而不是整个网络,就像在以太坊中每个交易都是由整个网络来处理的。这就是我们提高可扩展性的方法。正确的方法实际上来自于我们对编程和通信转换系统多年的扎实研究。将合约视为互相发送消息的自动机,我们可以制定我们关心的问题,例如合约是否最终会给我退款,是否执行过某个操作等等。这些我们关心的问题描述起来很简单,比如最近在以太坊Parity钱包发现的程序漏洞的问题就可以被很容易的描述,那就是是否有人可以通过潜在漏洞终止合约。如果我们写一个智能合约并且可以形式化地向以太坊证明这个合约永远不会出现问题,那么我们就在安全方面得到了保障。Zilliqa的这种构建合约的方式,使得我们在部署合约之前就可以确保合约的安全性。

January 10, 2019 · 1 min · jiezi

新年放大招:Github 私库免费了!

据《Github 嫁入豪门》这篇文章刚好半年了,那时候栈长还表示对 Github 的期待,仅仅半年,现在就已经有了巨大改变。微软果然是豪门,嫁入半年就已经开花结果了,免费私库已全面无限制开启……我们来看下 Github 的官宣:New year, new GitHub: Announcing unlimited free private repos and unified Enterprise offering新年,新的 Github, Github 宣布了两个重大更新:1、无限制免费私库(GitHub Free)即现在可以无限制的免费使用 Github 私有仓库,让开发人员更好的使用 GitHub,不过最多只有 3 个免费协作成员。2、统一的企业产品(GitHub Enterprise)Github 提供了更简单、灵活、统一的企业产品,通过 GitHub Connect 可以连接打通云仓库以及本地化仓库。下面我们来演示一下如何创建一个私库:如图所示,选择 Private 即可,之前这个选项是必须付费才能开放的,现在免费无限制放开了。我们再来看下如何添加协作者:如图所示,搜索需要协作的成员进行添加,需要对方同意。只能添加 3 个协作者,也只有被添加进来的协作者才能拥有项目的访问和提交权限,也可以取消协作成员。现在开始创建年轻人的第一个 Github 免费私库吧!更多详情:https://blog.github.com/2019-…据栈长所知,现在免费开放私库的产品有国内的码云、Coding,以及国外的 Gitlab 等,现在 Github 也加入免费行列,必定残食其他的市场份额。至于 Github 后续的发展,请持续关注Java技术栈微信公众号,栈长将第一时间跟进最新动态。你也可以在后台回复:动态,获取栈长整理的更多往期热门好玩的技术资讯。如果你对 Git 还不熟悉可以在Java技术栈微信公众号后台回复关键字:Git,获取往期栈长整理的 Git 系列文章教程。本文原创首发于微信公众号:Java技术栈(id:javastack),转载请原样保留本信息。

January 9, 2019 · 1 min · jiezi

如何在GitHub上大显身手?

推荐一篇良许大佬的文章,如何在github上大显身手。拥有自己的github,且有所贡献,这是一件很有意义的的事情,在面试上也是加分项哦,赶紧搞起来。转载至http://uee.me/aHAfN这推荐一个网站,收集了有趣、入门级的开源项目:https://hellogithub.com/码农朋友们都知道,gitHub是一个面向开源及私有软件项目的托管平台,上面托管了众多的优秀的项目,比如linux内核源码、git源码、机器学习框架tensorflow等等。当然,除了这些顶尖项目外,还有海量的高手开发的优秀项目。所有的这些项目,我们都可以为它贡献代码。那么,要如何为这些项目贡献代码呢?我们下面以实例来讲解。比如说,现在gitHub上有个很牛逼的项目githubTest,它的作者是Alvin,项目地址如下:https://github.com/yychuyu/githubTest现在Harry看到了这个项目,想要对它进行贡献代码。想要达到这个目的,Harry要完成下图的这些步骤:1. forkHarry在找到这个项目之后,点击右上角的「fork」按钮。稍等片刻后,就会在Harry的账号下克隆了一个一样的项目githubTest,包括文件,提交历史,issues,和其余一些东西。2. clone通过fork之后,Harry的账号下也有了githubTest这个项目,但还不能对它进行编译、修改(其实是可以修改,但是不建议)。这时,可以通过git clone命令,将这个项目clone到自己的电脑里。3. update a file & 4. commit接下来,Harry就可以大显身手了,可以自由对这个项目进行修改。但是,不建议在master分支直接修改,建议在master分支基础上切出一个dev分支,然后在dev分支上自由发挥。修改完之后,再将dev分支merge到master分支。5. push在自己的电脑上修改好代码之后,再使用git push命令将改动同步到自己的gitHub项目仓库里。通过这个操作后,就可以在代码仓库里看到自己的提交了。6. pull request接下来,就是向原作者Alvin提交你的代码了。首先点击文件列表上的「New pull request」。之后,gitHub会自动对源仓库分支及自己仓库分支代码进行比对,看看是否有冲突。如果它显示「Able to merge」的话,Harry就可以点击下面的「Create pull request」绿色按钮,进行代码提交。再之后,系统会要求你写一段注释,其实也可不写。但最好写一下,跟作者说明一下你改动了什么,为啥这么改。通过以上6步,原作者Alvin就会收到一个pull request,如下图:然后,Alvin可以点进去,看看Harry具体提交了一些什么修改。如果他觉得这个修改确实够niubility的话,它就可以点击「merge pull request」,将Harry的提交集成到自己的项目里。[图片上传中…(image-19e584-1547014948798-10)]至此,功德圆满,Harry顺利完成一次对项目githubTest的代码贡献。但是,这个项目实在太出众了,很多高手看到了并贡献了众多代码。比如现在Alvin自己提交了一个文件:现在原作者项目已经发生了改变,那Harry账号下的githubTest如何与原作者Alvin的项目保持同步呢?Harry需要做以下三步操作:1. fetch现在代码不同步了,我们要先把Alvin仓库的代码fetch到自己电脑的仓库下。注意,这是在自己电脑上操作,不是在github上操作。git fetch git@github.com:yychuyu/githubTest.git master:latest上面这条命令,git fetch 之后 的那部分,是原作者Alvin项目git地址,通过点击原项目「clone or download」按钮可以看到。再之后master:latest这部分,master是原项目分支,latest是自己项目分支。如果latest分支不存在的话,将自动创建。其实也可以将代码fetch到自己的master分支,但也不建议这么做。2. merge代码fetch到latest分支之后,再切到master分支,再使用git merge命令将最新代码合并到master分支。3. push现在,Harry电脑上的代码与原项目代码保持同步了。我们再使用git push命令,就可以将最新代码推到Harry账号下githubTest项目里以上的三个步骤具体操作过程如下图示。接下来,Harry就可以在此基础上,继续贡献自己的代码了。推荐阅读: IDEA热部署插件JRebel「 神器 」强大的系统清理工具 THANDKSEnd -一个立志成大腿而每天努力奋斗的年轻人伴学习伴成长,成长之路你并不孤单!

January 9, 2019 · 1 min · jiezi

阻碍区块链应用落地的五大难题和解决方案

2018年初区块链掀起了一阵新热潮,多家互联网公司纷纷宣布推出区块链项目,新兴的区块链项目方和媒体百家争鸣,一时之间区块链行业风光无限。区块链概念的火爆,使得越来越多的人开始学习它、理解它,甚至“拥抱”它。只是沉浸在“狂欢”里的众人怎么也没有想到之后坏消息接连而至。从一开始的币价大跌“”币价大跌开始,接连有项目解散跑路,一众空气项目接连有序走向归零,市场恐慌接连不断,人们不禁开始怀疑区块链是否真的有出路。2019 年人们已从开始的狂热转向理智。关于区块链,大家关注的不再只是区块链是什么,而是区块链能做什么?提起区块链的应用,最先想到的便是以比特币为代表的数字货币领域。但是随着 1C0 的爆发,数字货币的投机性被疯狂放大,甚至部分沦为诈骗工具。目前区块链技术真正的落地应用却很少,我们不禁要问,被众人寄予极大期望的区块链为何落地会如此艰难?一、为什么区块链应用落地困难?我们可以发现区块链在应用落地方面遇到的困境可能主要有以下 5 个方面,在非技术方面,它面临炒作和投机、认知和应用门槛高等问题,在技术方面,它面临性能无法与互联网媲美,以及无法与互联网进行数据交互等问题。1、投机与空气项目之前的“区块链狂热”现象,仅仅是一些人利用空气项目和 1C0/IF0/IM0 的热潮在投机炒作而已,这些人的目的只是圈钱而并非真正地去研究、应用区块链技术。一开始就动机不纯又如何做到真正发展区块链,早已为后期众多项目跑路和解散埋下伏笔。2、认知门槛偏高区块链对于普通人而言,存在很高的认知门槛。及时是对于大多数行业内从业者,对区块链的认知也参差不齐。区块链本身是一门跨学科的边缘技术,涉及密码学、分布式网络、计算机软件、博弈论等多个不相关的学科,且资料匮乏,技术发展又异常迅速,知识更新非常之快,要完全了解和掌握是很困难的,需要投入大量的时间和精力去进行研究。3、应用门槛偏高区块链应用门槛高主要体现在对用户不友好。大部分应用存在存在一定的操作门槛。首先,用户需要具备一定的区块链技术知识,就拿基础的数字货币交易来说,用户可能就需要知道“私钥是什么”、“助记词是什么”、“冷钱包和热钱包”等等,大部分第一次接触的用户无法在短时间之内通过自身理解与操作融会贯通。其次,对于开发者而言,技术门槛也偏高,目前链上应用(Dapp)的开发可能需要掌握一门新的语言(如Solidity),但目前此类教程并不多,而且网上缺乏完善的资料。导致开发的 Dapp 可能不够友好或者存在诸多漏洞。4、效率和性能不足我们通常将交易吞吐量(TPS)看作是区块链的性能指标,它表示在固定时间能处理的交易数。在实际应用中,如果 TPS 并发太低,容易造成网络拥堵严重,大量交易排队,从而使得区块链在高并发业务的场景下无法落地,甚至连目前我们要求的简单支付都是问题。5、无法与链外信息直接交互目前智能合约还无法主动与外界数据/信息进行交互,在智能合约的触发条件取决于区块链外信息时,这些信息需先写入区块链内,但是目前区块链还无法主动完成这一个操作。而智能合约在多数场景下,往往需要与外部世界进行数据交互,典型比如去中心化保险、稳定货币及借贷平台、预测市场、去中心化旅游等等。二、如何解决这些问题?针对非技术问题,如炒作圈钱,我们相信这个寒冬没有价值的项目终将被识破,并被剔除。而我们也可以看到目前主流交易平台已经开始了清理工作,逐渐下架一些不正规的项目代币。针对认知门槛高问题,可能并不是短时间之内可以解决的,或许也可能并不需要解决,就像互联网发展至今,大家都在使用微信和支付宝支付,但是完全可以不懂第三方平台是如何与银行进行结算,以及通过什么技术实现和保障资产安全的。区块链发展壮大之后也会降低普通用户的使用门槛。对于应用的技术门槛,一方面很多项目在推进更友好的开发环境和语言,让普通开发者可以直接上手。另一方面,目前已经很多团队在做相关的技术课程,比如我们深度合作的一块链习技术社区,在做以太坊的智能合约高阶课。随着越来越多的智能合约开发者参与进来,其社区相关内容的不断完善,这个门槛也会逐渐被开发者所接受。而效率和性能问题,这个也是大家最关注的,涌现出了多种解决方案及项目,比如状态通道、Plasma、Turebit 等 Layer 2 方案,像是 Celer Network、Raiden Network 等项目,当然也有很多团队在研究和开发更好的公链,比如ThunderCore、Dfinity、Algorand 等项目。对于区块链无法直接获取链外信息的问题,解决方案就是预言机。以太坊创始人 Vitalik Buterin曾在王峰十问的采访中也肯定了预言机的作用,Vitalik 表示:“I think that one component that needs to be built that can make smart contracts work better is oracles, for providing information to smart contracts about the outside world. I know that Oraclize has been working on centralized oracles for a long time, though I am also interested in the decentralized oracle projects. ”翻译:我认为,需要建立一个可以使智能合约更好地运作的组件预言机,为智能合约提供外部世界的信息。我知道 Oraclize 长期以来一直致力于中心化的预言机,尽管我对去中心化的预言机项目也很感兴趣。引用来源:https://www.yours.org/content…三、什么是预言机?预言机就是一种单向的数字代理,可以查找和验证真实世界的数据,并以加密的方式将信息提交给智能合约。预言机就好比区块链世界中的一个第三方数据代理商。举例来说,假设现实世界中的“数据源”和区块链中的“数据接口”,是两个使用不同语言的国家,预言机就是中间的翻译官。通过预言机智能合约就可以和链外数据进行无障碍交流。在实际使用智能合约中,去中心化的预言机可以保证提供的数据无法被篡改。进一步了解预言机请参考:区块链技术科普丨什么是预言机(Oracle)四、DOS Network 为什么要做预言机?我们在 2017 年就在关注以太坊 Dapp 的开发,但是却发现区块链应用无法主动与链外信息进行数据交互。我们意识到这是阻碍区块链生态发展和去中心化应用大规模普及的一个重要因素。经常调研之后,我们发现还没有可用的去中心化预言机服务,于是开始研究更好的解决方案。在找到更好的解决方案之后,我们便启动了 DOS Network 这个项目。其中 DOS 是 Decentralized Oracle Service 的缩写。它所提供的预言机服务可以连接智能合约和链外互联网世界,同时也为区块链提供无限的且可验证的计算力。DOS Network 在性能上可支持多条链,且数据结果接近实时,同时 DOS Network 分片的网络结构设计,可以并行处理请求,达到高性能和可扩展性,帮助扩展智能合约的处理能力,是一个二层(layer 2)网络解决方案。我们的核心技术团队位于硅谷,两位创始人王琦和华思远均毕业于美国卡耐基梅隆大学,并获电子与计算机工程专业硕士学位。华思远曾任职于 Amazon、Google高级软件工程师,王琦曾任职于 Oracle 总部、Pure Storage 高级软件工程师。作为区块链技术的早期爱好者且在硅谷从事技术开发工作多年,具有丰富的技术开发经验,对区块链行业也十分了解。我们相信 DOS Network 是一个有价值且非常有意义的项目,它可以让更好的提升区块链的可用性,我们也一直在为之努力。如果你是一位开发者,欢迎来试用 DOS 的 Oracle 服务,目前我们已经在以太坊测试网发布了 alpha 版本,2 月份我们会发布 beta 版本。(请复制到浏览器打开)https://dosnetwork.github.io/…如果你是区块链爱好者,也欢迎后期作为服务节点参与到网络中,可以获得 Token 奖励。我们也会在近期公布白皮书及其他细节。大家可以通过官方渠道持续关注我们,或直接添加dosnetwork001加入社区。本文图片皆来源于网络,版权属于原作者 ...

January 9, 2019 · 1 min · jiezi

作为软件工程师,如何进行知识管理

简评:对学到的知识进行整理归纳有助于温故而知新(以下我均指原作者 Bruno Paz)软件开发和技术总的来说是一个快速发展且需要持续学习的领域。在 Twitter、Medium、RSS feeds、Hacker News 等专业网站和社区上浏览几分钟,就足以从论文、案例研究、教程、代码片段、新应用程序等内容中找到大量的有用信息。收集和整理所有这些信息可能是一项艰巨的任务。在这篇文章中,我将介绍一些我进行知识管理的工具。我认为对知识管理非常重要的一点是避免局限在特定平台。我使用的所有工具都允许以标准格式(如 Markdown 和 HTML)导出数据。请注意,我的工作流程并不完美,我一直在寻找新的工具和方法来优化它。每个人的情况都是不同的,所以对我有用的东西不一定适合你。基于 NotionHQ 的知识库对我来说,知识管理的基础部分是建立某种个人知识库/维基,一个你可以系统地保存链接,书签,笔记等信息的地方。我用的工具是 NotionHQ 。我用它来记录各种主题,列了一些资源表,如用编程语言分组的优秀库或教程,为有趣的博客和教程添加书签等等,除了软件开发相关的知识,我还用它记录个人生活。我最喜欢 Notion 的地方是用它创建新内容非常的简单。你可以用 Markdown 格式编写,并将内容组织为树状结构。这是我的“开发”工作区的顶层页面:Notion 还有其他一些很好的特性,比如集成电子表格/数据库和任务板。由于免费功能有限,如果你想深度使用 Notion,就需要购买个人付费版,我认为它物有所值。Notion 允许将整个工作区导出到 Markdown 文件。导出存在一些重要问题,比如丢失页面层次结构,希望 Notion 团队可以改进这一点。如果要用免费的工具,我可能会选择使用 VuePress 或 GitBook。用 Pocket 保存有趣的文章Pocket 是我最喜欢的应用程序之一!使用 Pocket,你可以创建 Internet 上的文章阅读列表。每当我看到一篇有点意思的文章,我都会用 Pocket 的 Chrome 扩展将文章保存到 Pocket,等之后我从头阅读完,发现它足够有用,就用 Pocket 的“归档”功能永久保存该文章并清理我的 Pocket 收件箱。我尽量保持阅读列表足够小,并保存存档我处理过的信息。Pocket 允许你标记文章,以便以后能更轻松地搜索特定主题的文章。为了防止原始站点消失,你还可以将文章的副本保存在 Pocket 服务器中,要使用这个功能需要购买 Pocket Premium。Pocket 还有一个“发现”功能,它会根据你保存的文章推荐类似的文章,这是寻找新内容的好方法。使用 SnippetStore 进行代码片段管理从 GitHub 到 Stack Overflow answers,再到博客文章,常常能找到一些你希望稍后保存的好代码片段。它可能是一些很好的算法实现,一个有用的脚本,或者一个如何在 Y 语言中执行 X 的例子。我尝试了很多应用程序,从简单的 GitHub Gists 到 Boostnote,直到我发现了 SnippetStore。SnippetStore 是一个开源的代码片段管理应用程序。SnippetStore 与其他产品的区别在于它的简单性。你可以按语言或标签组织整理代码片段,并且可以有多个文件片段。它不完美但足以完成我需要的工作。Boostnote 虽然具有更多的功能,但我更喜欢 SnippetStore ,它组织内容的方法的更简单。对于我每天使用的缩写和片段,我更喜欢使用 Editor/IDE 片段功能,因为它更方便。我使用SnippetStore 更像是对代码示例的参考。Cacher 也是一个有趣的选择,因为它集成了许多编辑器,有一个 cli 工具,使用 GitHub Gists 作为后端,但是付费版每个月需要 6 美元,个人认为有点贵。使用 DevHints 管理备忘单Devhints 是由 Rico Sta. Cruz 创建的备忘录表的集合。它是开源的,由 Jekyll 提供支持,Jekyll 是最受欢迎的静态站点生成器之一。备忘单是用 Markdown 编写的,带有一些额外的格式优势,比如对列的支持。我非常喜欢这个界面的外观,Markdown 使得添加新内容并保持更新和版本控制变得非常容易,这与 PDF 或 Image 格式的备忘录不同,这种格式的可以在 Cheatography 等网站上找到。因为它是开源的,所以我创建了自己的分支,删除了一些我不需要的备忘单,并添加了一些。我使用备忘单作为「如何使用一些库或编程语言或记住一些命令」的参考。拥有一个单独的页面非常方便,例如具有特定编程语言的所有基本语法。我仍然在试验这个工具,到目前为止用的挺好。DiigoDiigo 允许你注释和突出显示网站的部分内容。我在研究新主题时用它来注释重要信息,或者从文章、Stack Overflow answers 中保存特定段落或者从 Twitter 中获取带来灵感的引用。以上就是我想介绍的内容。某些工具可能在功能方面有一些重叠,但是正如我在开始时所说的,这是一个不断发展的工作流程,因为我一直在尝试和寻找改进和提高生产力的方法。你是如何管理你的知识的呢?在评论区一起聊聊吧。原文链接:How I organize my knowledge as a Software Engineer ...

January 9, 2019 · 1 min · jiezi

Zilliqa计费单价

Zilliqa的基础计费单位是Zil。但是,Zilliqa支持最多12位小数的基本单位。Zilliqa 共有3个不同的计费单位:Zil,Li,Qa。Zilliqa的燃料计费Zilliqa的gas计费与以太坊的格式相同。有一个gasLimit(单个区块允许的最多gas总量)是根据处理交易过程中涉及到的计算的复杂程度来设定的,另有一个gasPrice(面额是Li)是由自由市场决定。注意:简单的用户帐户到帐户(account-to-account)交易会花费1gas的费用。有关智能合约(Scilla)功能的Gas花费的文档可以在以下pdf中找到:https://drive.google.com/file/d/1c0EJXELVe_MxhULPuJgwGvxFGenG7fmK/view?usp=sharing 12Zilliqa的引导阶段的 gas费用在Zilliqa网络的引导阶段,最小值gasPrice将设置为1000Li。用户可以使用JSON-RPCAPI 查询当前DS时期的最新的gasPrice值。根据2019年1月6日的CMC 数据,这相当于$0.0000205。因此,简单的用户帐户到帐户(account-to-account)交易的gas费用将大致花费:$0.0000205 * 1 gas = $0.0000205对于基于合约的交易,通过参考上面的pdf文档,简单地将gasPrice乘以每个合约转换所需的gasLimit。你也可以通过Web IDE或本地编译的Scilla二进制文件测试你的转换并获得静态gas费用。

January 8, 2019 · 1 min · jiezi

解决packet_write_wait: Connection to...: Broken pipe

问题打算从远程仓库克隆项目到本地时,报错:$ git clone git@github.com:EverChan6/meetingDemo.gitCloning into ‘meetingDemo’…packet_write_wait: Connection to 52.74.223.119 port 22: Broken pipe // <==就是它!!!fatal: Could not read from remote repository.Please make sure you have the correct access rightsand the repository exists.解决1. 从网上看了挺多解决办法,终于找到一个说辞是说由于多日未 进行ssh 登录操作,本地 publickey 失效造成的。所以解决方法就是Adding your SSH key to the ssh-agent$ ssh-add ~/.ssh/id_rsaIdentity added: /c/Users/HP/.ssh/id_rsa (/c/Users/HP/.ssh/id_rsa)2. 如果执行上面的命令出现如下错误Could not open a connection to your authentication agent.则先执行以下命令$ ssh-agent bash

January 8, 2019 · 1 min · jiezi

浅谈SLAM的回环检测技术

什么是回环检测?在讲解回环检测前,我们先来了解下回环的概念。在视觉SLAM问题中,位姿的估计往往是一个递推的过程,即由上一帧位姿解算当前帧位姿,因此其中的误差便这样一帧一帧的传递下去,也就是我们所说的累计误差。我们的位姿约束都是与上一帧建立的,第五帧的位姿误差中便已经积累了前面四个约束中的误差。但如果我们发现第五帧位姿不一定要由第四帧推出来,还可以由第二帧推算出来,显然这样计算误差会小很多,因为只存在两个约束的误差了。像这样与之前的某一帧建立位姿约束关系就叫做回环。回环通过减少约束数,起到了减小累计误差的作用。那么我们怎么知道可以由第二帧推算第五帧位姿呢?也许第一帧、第三帧也可以呢。确实,我们之所以用前一帧递推下一帧位姿,因为这两帧足够近,肯定可以建立两帧的约束,但是距离较远的两帧就不一定可以建立这样的约束关系了。找出可以建立这种位姿约束的历史帧,就是回环检测。回环检测的意义有了前端的视觉里程计及后端优化的SLAM系统,似乎已经比较好用了。但其在提高实时性的同时精度却降低了,一旦精度降低,又会面临长时间累计误差的问题,特别是像ORB-SLAM那样只做局部地图优化的方案。我们该如何平衡这个矛盾呢?我们不妨先思考下,在一个陌生的环境中,我们人类是如何进行环境地图的建立?在局部区域,人不断的移动从而在脑海中建造增量式地图,时间长了大部分人也分不清东南西北了,与起始点的关系又如何。假如人正巧回到了之前路过的位置,在对环境足够敏感的情况下,他就能发现这个事实,从而修正自己之前对方位的判断。我们说,此时检测到了一个回环,显然,人可以通过面前看到的景象与脑海中残缺的印象来对比从而检测到回环的,对于SLAM来说也可以通过对比当前帧与过去关键帧的相似度,如相似度超过某一阀值时就可以被认为是检测到回环。现在,问题的关键就在于如何判断两帧图片的相似度。最直观的做法是特征匹配,比较匹配的数量是否足够多。但由于特征匹配非常耗时,回环检测需要与过去所有关键帧匹配,这个运算量是绝对无法承受的。因此,有人提出了词袋模型,用来加速特征匹配。什么是词袋模型?词袋模型就是把特征看成是一个个单词,通过比较两张图片中单词的一致性,来判断两张图片是否属于同一场景。为了能够把特征归类为单词,我们需要训练一个字典。所谓的字典就是包含了所有可能的单词的集合,为了提高通用性,需要使用海量的数据训练。字典的训练其实是一个聚类的过程。假设所有图片中共提取了10,000,000个特征,可以使用K-means方法把它们聚成100,000个单词。但是,如果只是用这100,000个单词来匹配的话效率还是太低,因为每个特征需要比较100,000次才能找到自己对应的单词。为了提高效率,字典在训练的过程中构建了一个k个分支,深度为d的树,如下图所示。直观上看,上层结点提供了粗分类,下层结点提供了细分类,直到叶子结点。利用这个树,就可以将时间复杂度降低到对数级别,大大加速了特征匹配。使用DBoW3库训练及使用词典DBoW3库为我们提供了非常方便的训练词典和使用词典的方法。训练词典时,只需要把所有训练用的图片的描述符传给DBoW3::Vocabulary的create方法就可以了。训练好的词袋模型保存在vocabulary.yml.gz文件中。接下来,使用训练好的词袋模型对图片计算相似性评分。DBoW3为我们提供了两种计算相似性的方式,第一种是直接对两张图片比较;第二种是把图片集构造成一个数据库,再与另一张图片比较。005QQQghzy7oCR8cSOZ31&690可以看出,图片越相似,评分越接近1。我们可以根据这个评分来判断两张图片是否是同一场景。但是直接给定一个绝对的阈值并不合适。通常,如果当前帧与之前某帧的相似度超过当前帧与上一个关键帧相似度的3倍,就认为可能存在回环。不过,这种做法要求关键帧之间的相似性不能太高,否则无法检测出回环。

January 8, 2019 · 1 min · jiezi

Neo私链

为了研究Neo的代码,需要看一下Neo到底怎么运行的,在网络上跑一个智能合约,并且调试该网络。所以先搭建一个私链玩玩。配置虚拟机NEO 私有链的部署至少需要 4 台服务器才能取得共识,每台服务器对应一个共识节点。在阿里云上可以买按量付费的虚拟机(机器都是window server)。记录下四台机器的内网IP地址,后面要用。172.xxx.xxx.142端口如果你想让外部程序访问该节点的 API 需要开放防火墙端口:10331-10334, 20331-20334阿里云的vpc如果在一个安全组内就不需要设置。也可以通过安全组开放所有入口。阿里云安全组安装运行时运行环境运行 NEO 节点需要安装 .NET Core Runtime,需要安装 1.0.1 或以上版本Windows 系统下的安装方法在 Windows 系统下安装 .NET Core 非常方便,直接下载运行即可。NOTEWindows可能还需要安装Microsoft Visual C++ 2015 Redistributable Update 3(https://www.microsoft.com/en-… ,否则后面无法启动Neo-CLI。创建钱包在四台机器上分别创建钱包数据库,数据库文件会默认生成在neo-cli的目录下面 create wallet wallet.jsondb3后缀名的数据库文件不支持了neo1neo> create wallet wallet.db3Wallet files in db3 format are not supported, please use a .json file extension.neo> create wallet wallet.jsonpassword: ********password: ********address: AS43TUy7VtuGpNJh2YC2NM3asTv3s6piyk pubkey: 02d57c9bbc42b45943867dff0180cb9df266b46db7d027a283eabc7c4b8819c86c记录下另外几台机器的钱包公钥neo2address: AY35ZjJayg9JfmCeHg1nySN4LSMntFEq2h pubkey: 035022ce81faca930ebe6b9f8e830526150e47692f19f58affe1311e42cd0d43bbneo3address: AQZNtdGVXbfot5fSaR4ijBqrEwbnFQspmH pubkey: 029f21d56f9422e55f0ef7cf75c092854e01c99aeee73f0effeaa489a96fc7d2b1neo4address: AXctyT8ctWTXtWaUFM9o5FJas8BWdBbiqe pubkey: 02dc0e750e21c386537618079d89836eea39fad396206e2e56cfba16bdc014dc5d修改节点的配置文件{ “ProtocolConfiguration”: { “Magic”: 1, “AddressVersion”: 23, “StandbyValidators”: [ “02d57c9bbc42b45943867dff0180cb9df266b46db7d027a283eabc7c4b8819c86c”, “035022ce81faca930ebe6b9f8e830526150e47692f19f58affe1311e42cd0d43bb”, “029f21d56f9422e55f0ef7cf75c092854e01c99aeee73f0effeaa489a96fc7d2b1”, “02dc0e750e21c386537618079d89836eea39fad396206e2e56cfba16bdc014dc5d” ], “SeedList”: [ “172.24.198.142:10333”, “172.24.198.141:10333”, “172.24.198.140:10333”, “172.24.198.139:10333” ], “SystemFee”: { “EnrollmentTransaction”: 0, “IssueTransaction”: 0, “PublishTransaction”: 0, “RegisterTransaction”: 0 } }}打开钱包,开启共识open wallet wallet.jsonstart consensus如果前面打开过cli,请删除chain目录下的文件,这样区块链高度会从1开始。截取其中一个打印,后面方便调试的时候研究neo> open wallet wallet.jsonpassword: ********neo> start consensus[18:04:31] OnStart[18:04:31] initialize: height=1 view=0 index=0 role=Backup[18:04:47] OnChangeViewReceived: height=1 view=0 index=2 nv=2[18:05:01] timeout: height=1 view=0 state=Backup[18:05:01] request change view: height=1 view=0 nv=1 state=Backup, ViewChanging[18:05:58] OnPrepareRequestReceived: height=1 view=0 index=1 tx=1[18:05:58] send perpare response[18:06:01] OnPrepareResponseReceived: height=1 view=0 index=2[18:06:01] relay block: 0x8515184285fa2e454b0eca441580b5cce01cc0e5cb5dd6d7ff53ef13ec7f665e[18:06:01] persist block: 0x8515184285fa2e454b0eca441580b5cce01cc0e5cb5dd6d7ff53ef13ec7f665e[18:06:01] initialize: height=2 view=0 index=0 role=Backup[18:06:16] OnPrepareRequestReceived: height=2 view=0 index=2 tx=1[18:06:16] send perpare response[18:06:19] OnPrepareResponseReceived: height=2 view=0 index=1[18:06:19] relay block: 0x35b163f4f524a1ebcc1ef3053e878f9bb37b1f4a8531a060ef18cfd6efefdf45[18:06:19] persist block: 0x35b163f4f524a1ebcc1ef3053e878f9bb37b1f4a8531a060ef18cfd6efefdf45[18:06:19] initialize: height=3 view=0 index=0 role=Backup[18:06:49] timeout: height=3 view=0 state=Backup[18:06:49] request change view: height=3 view=0 nv=1 state=Backup, ViewChanging[18:06:49] OnChangeViewReceived: height=3 view=0 index=1 nv=1[18:06:53] OnChangeViewReceived: height=3 view=0 index=2 nv=1[18:06:53] initialize: height=3 view=1 index=0 role=Backup[18:06:55] OnPrepareRequestReceived: height=3 view=1 index=2 tx=1[18:06:55] send perpare response[18:06:55] OnPrepareResponseReceived: height=3 view=1 index=1[18:06:55] relay block: 0x1766a0602903f513f76561c93fddf312f70d30801310abfecacc89feace5c412[18:06:55] persist block: 0x1766a0602903f513f76561c93fddf312f70d30801310abfecacc89feace5c412[18:06:55] initialize: height=4 view=0 index=0 role=Primary[18:07:10] timeout: height=4 view=0 state=Primary[18:07:10] send perpare request: height=4 view=0[18:07:13] OnPrepareResponseReceived: height=4 view=0 index=3[18:07:13] OnPrepareResponseReceived: height=4 view=0 index=2[18:07:13] relay block: 0x5233d9086db97e59f86d73e8735093c1b3fe4569759697ff8a04e0eb8f389091[18:07:13] persist block: 0x5233d9086db97e59f86d73e8735093c1b3fe4569759697ff8a04e0eb8f389091[18:07:13] initialize: height=5 view=0 index=0 role=Backup提取Neo原来文章中有提取neo和gas的方法,但是我现在还不知道为什么要提取Neo,有什么意义,先跟着文档跑着看看。我用的window server会遇到crash,详情请看https://github.com/neo-projec…希望Neo社区的大牛能解决一下这个问题。一天后,社区给出的解决方案https://github.com/neo-projec…,需要下载两个包结论虽然配置成功了,但是还不知道能干吗,需要进一步研究。提取Neo也遇到崩溃的情况,发现这个问题,也算做贡献了。参考文章基于NEO的私链(Private Blockchain)用 NEO 节点搭建私有链作者:沈寅链接:https://www.jianshu.com/p/df4… ...

January 7, 2019 · 2 min · jiezi

git常见问题解决

记录在开发过程中遇到的git问题,边解决问题边学习,常见的命令后续找时间再补充,尽量写一个完整的教程。git处理冲突当拉取下来的文件与本地修改的文件有冲突,先提交你的改变,或者先将你的改变暂时存储起来1、将本地修改存储起来git stash2、pull内容 git pull3、还原暂存的内容git stash pop stash@{0}也可以简写git stash pop参考:https://www.cnblogs.com/wteam…git另一个进程还在运行问题描述出现这种情况可能是git在执行的过程中,你中止之后异常,进程一直停留Another git process seems to be running in this repository, e.g.an editor opened by ‘git commit’. Please make sure all processesare terminated then try again. If it still fails, a git processmay have crashed in this repository earlier:remove the file manually to continue.问题原因因为进程的互斥,所以资源被上锁,但是由于进程突然崩溃,所以未来得及解锁,导致其他进程访问不了。问题解决打开隐藏文件夹选项,进入工作区文件目录的隐藏文件.git,把其中的index.lock问价删除掉

January 6, 2019 · 1 min · jiezi

打造 10000 Star 的前端开源项目 ⭐

在工作学习之余,你可能会萌生做一个开源项目的想法。一方面将自己的好代码分享出去帮助更多开发者,另一方面也希望在开源社区中得到反馈和成长。如果项目能获得很多的关注那更是锦上添花,高 Star 不仅是衡量开源项目可靠程度的一个重要依据,这样项目维护者的 Github 也能在招聘中让公司提前了解候选人的开源贡献、技术热情和编程习惯等,获得面试官的加分。那么,开源项目怎么才能获得更多的 Star 数呢?这里通过总结我这段时间维护 Day.js 项目的过程中的一些经验教训,来说说如何改进和推广你的开源项目。瞄准用户痛点开源社区的内容包罗万象,整理收藏的 Markdown 笔记、 框架全家桶的搭建、炫酷的动画效果以及各种工具库、框架等都是很好的开源方向,但是考虑到项目的功能、受众、开发语言等等因素,不同类型的项目实现起来的难度和被社区接受的程度也千差万别。但如果项目能瞄准开发者的痛点,提供优秀的解决方案,就有获得更多关注的可能。一个人的精力始终是有限的,只有更多的人加入进来,使用、反馈、迭代和推广,才能形成开源项目的良性循环。比如我在工作中发现 Moment.js 虽然能很方便地处理日期和时间但这个库打包体积太大了,而要想迁移到社区其他几个轻量的时间库又会增加新的学习成本和迁移工作量。所以开发 Day.js 的目标就是做一个和 Moment.js 一样 API 设计,一样功能,更加轻量的时间日期库。选择开源协议相较与项目本身的代码和文档等,开源协议可能是一个容易被忽视的细节。开源协议是软件的授权许可,表述了用户获得你开源的代码后拥有的权利和义务。我在初期推广时就犯了个错误,没意识到开源协议的重要性,也没有给项目添加任何协议。在版权意识相对更强的英文社区宣传时就遇到了很大的阻力和各种质疑,他们觉得这样的项目是不专业的,也不敢去轻易尝试,就这样白白错失了一部分初始用户。关于怎么去选择一个适合项目的开源协议,可以参考这个网站 Choose License。如果你希望项目可以尽可能被广泛地推广、使用和传播,就可以考虑选择一个分发自由度比较高的开源协议。规范提交记录使用一个规范的 Git 提交记录是很有必要的,这不仅让多人开发中的参与者能更好地了解项目的迭代历史和进程,也能在出现问题时快速定位,找到问题代码的提交记录。同时我们还可以使用工具根据提交记录自动生成更新说明 (CHANGELOG),方便用户了解每次更新的具体内容,也免去了项目维护者手动更新的痛苦。目前前端社区中使用较多的 Git Commit 提交规范是 Angular 规范 (Git Commit Message Conventions ),Commit 的格式包含 Header、Body、Footer 三个部分:<type>(<scope>): <subject><body><footer>但如果每次提交代码的 Commit Message 都需要我们按照上述格式来录入的话还是一件又累又容易忘记的苦差事。推荐两个工具来辅助我们的操作:使用 commitizen 进行交互式的 Commit 填写,如下图所示,只需要按照提示选择更新的 type 和填写必要的信息,就能自动生成符合规范的提交记录;使用 @semantic-release/changelog 来根据 Commit 中 type 自动增量生成 CHANGELOG;语义化版本号每个社区都有自己的版本号规范,千万不能因为是自己的开源项目就随心所欲地填写版本号,不然可能会给使用者带来不必要的麻(彩)烦(蛋)。在 NPM 生态圈中绝大部分包都是使用语义化版本号 (Semantic Versioning),即版本号为 a.b.c 的形式,其中 a 是大版本号,b 是次版本号,c 是修订号。如果开源项目有按上文所述规范地提交 Commit ,就可以使用 semantic-release 来实现全自动更新版本号和发布,这个工具会判断 Commit Message 的不同,fix 增加修订号,feat 增加次版本号,而包含 BREAKING CHANGE 的提交增加大版本号。推广和分析酒香也怕巷子深,再精美的项目,如果作者自身没什么知名度,又没有太多推广的话,这个项目很有可能就被淹没在众多开源项目之中了。除了在众所周知的国内开发社区推广之外,希望大家也不要忽视了国外的社区和论坛。要知道虽然中文开发者数量越来越多,但也只占到全球开发者的一部分,为了获得更多关注,我们有必要把开源项目国际化,来帮助更多的开发者。而英语是软件开发者们的通用语言,翻译一份英文版的 README 就是走向国际化的第一步。在推广 Day.js 的过程中,我会在 Twitter 大佬们吐槽 Date 函数、 Moment.js 库的推文下,介绍我的项目的特点,希望他们可以尝试一下(但要有礼貌,广告别太硬)。社区红人的一个 Star 或一条支持的推文就能依靠社交网络快速传播,给项目带来巨大的流量和很高的关注度。在每次的重大功能更新或集中推广之后,我们要通过项目的 Pull request, Issue, Star, Download Count 等数据的变化来了解推广效果和用户的满意度。前端工程师都知道,比起一堆数字,可视化的数据图表可以帮助我们更好地理解数据。如 npmjs.com 展示下载量变化的折线图,可以分析项目被使用和被依赖的情况。bestofjs.org 展示了项目 Star 数变化的日历色块图,格子越深说明增量越快。下图深色的小块就是项目几次比较成功的推广,而有些推广并没有带来很大的增长就需要总结经验并改善方法了。开始做一个开源项目并不难,要勇敢地迈出自己的第一步。但是持续维护开源项目并不是一件很容易坚持下来的事,我们需要找到自己维护项目的动力,给用户提供必要的支持,收集用户的反馈,不断完善项目,进而形成一个完整的产品闭环来推动项目的不断迭代更新。当然能做到这些, Star 数量的多少已经不是那么重要了,我们丰富了开源社区的内容,帮助了更多的开发者,也从开源的经历中得到了视野的拓展,能力的提升,这才更有价值的收获。P.S. 如果你热爱前端,热爱开源,欢迎加入我们团队,这里有网红开源 UI 库 Element,承接了公司 98% 前端项目的发布系统,比 jsdeliver 更好用的静态资源管理平台和更多有意思的项目等着你。请联系 kun.zhu@ele.me ,饿了么大前端有你更精彩。 ...

January 3, 2019 · 1 min · jiezi

2018:Zilliqa年度回顾

2018年12月25日Xinshu Dong发布于Zilliqa博客在2018年即将结束之际,让我们一起回顾2018重要的大事、共同展望2019。毫无疑问,2018年是Zilliqa最激动人心、最重要的年份之一。我们整个团队一直在为打造一个安全、去中心化、可扩展的区块链平台而稳步前进,并即将推出主网。我们的重要里程碑过去一年里,我们一直积极致力于实现新的功能,并改进平台上的现有功能。我们已将大型分片区块链系统的主体部分完成落地,并上线了向外部矿工开放的“猫山王”公共测试网。我们还完成了一个新的把安全作为设计基础的智能合约语言Scilla。通过合作和建立伙伴关系,我们开始在数字广告、保险科技、数字内容和游戏领域推出一系列有趣和创新的用例。在邀请公共矿工加入之前,我们还与许多开发人员进行了密切合作。他们帮助引导了我们的网络、测试各项功能和开发各种工具,以确保我们在主网启动时有一个足够强大的生态系统。我们积极参与世界各地的活动,并通过Scilla黑客马拉松和研讨会与热情的开发者社区进行了互动。除了技术方面的进展,我们还积极地与行业伙伴和更广泛的区块链生态系统进行合作。我们一直与传立传媒Mindshare在“质子项目”上进行合作。这个项目将展示Zilliqa如何通过区块链技术解决如结算和透明度等一些程序化广告方面的挑战。今年我们还启动了“Zilliqa生态构建资助计划”,以支持开发者积极参与Zilliqa生态系统构建。已有的资助项目包括钱包、开发者和用户工具,现在我们还在寻找可扩展性应用程序(例如第2层解决方案)、游戏SDKs、去中心化应用和Scilla附件。最大的挑战和心得尽管在项目成立之初我们已经有了一个研究原型和受许可的平台,但构建一个分片公链系统在规模、安全性和效率方面仍然有许多技术难题需要克服。由此我们体会到,必须注重在提升开发和质量保证方面的投资,才能使开发工作更高效。从一个5人的团队成长到20人以上本身就是一个巨大的挑战,但是很高兴我们的开发人员和业务团队人数在过去一年都有了很大的增长。在这样一个充满不确定性、过度宣传但也有巨大机会的新兴行业,找到最合适的人才是很困难的。我们已经认识到,需要更多的教育、培训和动力来激发优秀人才去喜爱并为区块链行业的发展作出贡献。我们另外一个发现是,使用一种新语言为一个新的区块链编写智能合约是很困难的,并且开发人员需要配备更多的工具、额外的文档以及内容丰富的教程才能在我们的链上做开发。Zilliqa接下来要做什么?2019年我们最大的里程碑之一是发布主网。这是一个备受期待的时刻,它将让支持我们的社区成员、合作伙伴以及整个行业见证我们过去一年的工作,并展示我们的研究如何应用在现实生活中。现实应用是2019年的一个重要关键词,因为我们的主要关注领域将是真实的增值用例。我们将通过向有好想法的开发人员提供资助,对有前途的公司进行战略投资,以及加速建立与企业的伙伴关系来实现这个目标。今后,我们将在科研方面继续投资,以便我们能够持续创新和推动技术进步。从长远来看,这将推动整个区块链技术的发展和应用普及。致我们的社区在这里,我们想要衷心感谢积极参与项目启动、筹款和发展的支持者们,谢谢你们一路同行陪我们走过高潮和低谷!我们还要感谢我们的社区开发者,在我们致力于提升开发工具的功能和用户友好性的同时,他们不知疲倦地为Zilliqa构建工具,并使用Scilla编写智能合约。展望未来,我们期待你们更多地参与到测试网挖矿以及Zilliqa钱包的额外测试中。在新的一年里,你们也可以在主网启动和代币映射的过程中保持冷静,这也将是对我们的莫大支持和帮助,我们在此提前表示由衷感谢!我们相信,区块链技术背后的真正力量在于它的社区,你们的参与对建立和发展我们的生态系统意义重大。你们可以通过多种方式参与,包括贡献优秀的DApp创意、搭建工具和DApp、挖矿以及使用Zilliqa上的DApp和游戏。最后但最重要的是,感谢你们让2018成为Zilliqa团队难忘的一年。我们期待着一个更好的2019年!

January 3, 2019 · 1 min · jiezi

Zilliqa进度更新第22期

2018年11月27日Yiling Ding 发布于Zilliqa博客,Rita译11月以来,我们工作的重中之重就是开发,从而确保Zilliqa主网在2019年1月31日准时上线。我们一方面努力筹备明年1月底主网上线的目标,另一方面尽全力准备下周即将发布的3.0版测试网。这一版本的公测网具有公开挖矿的功能,挖矿所得为Zil测试币。大家只要有兴趣、有条件就可以参与到公开挖矿中来,测试挖矿系统、演练挖矿流程,从而为即将到来的主网挖矿做好准备!我们即将发布有关更多的挖矿文件,更多细节敬请期待!质子计划(Project Proton)质子计划是Zilliqa与合作伙伴传立媒体Mindshare联合其他多家媒体行业领头企业如纽约数字媒体交易资讯公司Mediamath、纽交所上市公司Rubicon Project、国际化软件公司Integral Ad Science等共同发起的,旨在通过区块链技术解决数字广告行业痛点和挑战的一个试点项目。我们的合作伙伴传立媒体是全球顶尖的媒体和营销服务公司,隶属于群邑集团(Group M,网址:https://www.groupm.com/),在116个国家和地区中聘有超过7000名员工,全球年收入345亿美元,客户包括联合利华、IBM、沃尔沃、汉莎航空、劳力士等。本周,我们与传立媒体共同在新加坡数字工业日活动(Singapore Digital Industry Day)上向大家详细介绍了质子计划。我们计划于2018年底前整合项目数据库并完成智能合约的部署。心书与传立媒体亚太区首席运营官Gowthaman Ragothaman共同展示质子计划生态资助金近期,我们公布了第二批“Zilliqa生态构建资助金”的参与项目,其中包括钱包、浏览器、开发和用户工具等。对于第三批参与项目,我们将积极寻求Layer2应用、SDK游戏、分布式应用、Scilla新增功能等项目。社区AMA自由问答交流11月15日,我们在Zilliqa官方的Reddit和电报群举办了详细的社区AMA自由问答交流,共收到并回答了100多个来自社区的提问。我们现已将所有问题和答复整理成了word文档供大家参考,欢迎点击查阅:https://drive.google.com/file…。再次感谢所有关注和参与AMA的社区成员们!同往常一样,如您想要了解Zilliqa的更多信息或与我们讨论项目技术,请随时通过以下官方渠道与我们联系:电报群:https://t.me/zilliqachatSlack: https://invite.zilliqa.com推特:https://twitter.com/zilliqaReddit:https://www.reddit.com/r/zill…Github:https://github.com/Zilliqa/zi…Gitter:https://gitter.im/Zilliqa/eco…,包括“生态构建资助计划”)已举办的活动CRYSTAL中心Scilla研讨会我们在新加坡国立大学CRYSTAL中心(网址:https://crystal.comp.nus.edu….。CRYSTAL中心是新加坡国立大学的区块链研究和技术智库。在此次研讨会上,我们的首席语言设计师Ilya Sergey向大家介绍了Scilla背后的设计原则,并深入探讨了System F语言如何有利于智能合约领域。Ilya Sergey在新加坡国立大学CRYSTAL中心介绍Scilla"区块链的未来FoB"系列活动近期,我们不断加强对在伦敦举办活动的重视,并举办了为期3个月的“区块链的未来”(Future of Blockchain,简写FoB)系列竞赛活动,重点吸引牛津大学、剑桥大学、帝国理工学院、伦敦政治经济学院、伦敦大学学院师生的参与。活动开始于2018年11月下旬,闭幕活动将于2019年3月下旬在牛津大学举办。更多详情可查阅:https://medium.com/future-of-…活动注册截止日期12月9日:https://www.futureofblockchai…12月7-9日,新加坡ETH Singapore黑客马拉松活动,链接:https://ethsingapore.co/技术进展在技术方面,我们冻结了将与主网同步上线的一系列功能,从而进行大规模的广泛测试,燃料费定价机制也已实施。更多细节如下:燃料费定价机制此前,我们的网络在处理交易时已把燃料费的消耗和限制计算在内,但实际的燃料费定价机制是在近期实现的。我们新编写的燃料费定价机制工作流程如下:首先,矿工们在提交工作量证明PoW时也同步提交愿接受的最低燃料费价格;之后,DS委员会就即将到来的DS周期内网络可接受的整体最低燃料费价格达成共识;最后,网络仅接受燃料费价格大于等于既定最低价的交易。Gossip哈希的预传输我们观察到,我们现有Gossip协议的一个效率不高之处在于,每一轮整个消息都被传输到所有节点随机分配的点,导致节点可能多次接收同样的消息,并会在首次接收后丢弃之后重复的消息。所以经过改进,我们现在仅预先发送消息的哈希值并且要求接收节点在之后再请求实际消息。通过这个方法,我们能够明显减少不必要的网络数据量。改进和更新的功能除了对Gossip哈希预传输的改进外,我们还根据正在进行的测试结果继续对其余代码库进行改进。例如,我们在系统测试期间观察到,一些节点如果未能被分配到任何网络分片中时,可能出现一直等待不会到来的DS块现象,从而被卡住。经过改进,我们引入了基于计时器的序列,即如果节点在提交几分钟后没有收到块,那么该节点将重新加入网络,也就是主动获取最新的DS块并在下个机会时重新尝试挖矿。最近的另一项改进是将我们的Ethash库更新到最新版本(版本0.3)。过去几个月,我们一直使用的是旧版本,而为了解决几个已知的漏洞,迁移到新版本十分有必要。此外,新版本还附带了一个更易于使用的功能界面。Scilla解释器过去两周,我们完成了现金流分析器的首个原型以及静态燃料费分析器原型。这些分析器的目的是帮助开发人员检测合约中资金流动中的错误,并静态估算调用合约中的转换成本。我们将在未来几周内完成一些后续任务:清理、重构、归档代码。支持燃料费分析器中的部分应用,使分析器输出的信息更具可读性。在Savant IDE(地址:https://savant-ide.zilliqa.co…。我们很高兴与大家分享,我们已成功使用现金流分析器检测到Scilla合约中的一些错误。之后,我们将就此撰写专门的博客文章,详细解释这些分析器及其优点。开发工具和测试随着Zilliqa主网首个版本上线日期不断临近,我们已将核心协议进行冻结,我们的开发工具亦是如此。因此在过去两周,我们非常敬业的社区开发人员和团队成员(甚至是团队的非技术成员)都对JavaScript库进行了高水平的内部测试!使用这个反馈循环,我们已经迭代了现有的API,改进了开发人员的体验并修复了我们自己的工具。JavaScript库在我们新开发的内部CLI中也有很多用途,例如用于提供更全面、更自动化的各种规模的网络负载测试方法。截至目前,我们已经测试了I类和II类的非拜占庭交易(详情可阅读:https://blog.zilliqa.com/prov…,并将很快转向III类交易以及恶意行为。随着Scilla也日益临近主网发布,我们还打算在未来4至6周内对Savant IDE进行最后更新,来帮助开发人员运行有关工作。Zilliqa新闻报道Decentralised Podcast对心书的采访: https://decentpod.io/2-3-xins...Asian Scientist上关于Zilliqa首席科学官Prateek Saxena 的文章: https://www.asianscientist.co…日文对Zilliqa和Layer X合作伙伴关系的报道: https://jp.cointelegraph.com/…关于Zilliqa生态构建工作的报道: https://coindiligent.com/zill…如果您支持和信任Zilliqa,欢迎转发到朋友圈,让更多的人认识Zilliqa。如果您对项目有什么疑问,欢迎到评论区留言,我们会及时、认真回复每一个问题!

January 3, 2019 · 1 min · jiezi

Github Page搜索工具更新 - 收藏,手气不错

收藏功能用户可以保存自己喜欢的Github Page。该功能主要帮助用户在发现自己喜欢的Github Page的时候可以将这些Github Page记录下来,翻遍以后查找。手气不错功能每次使用手气不错功能,都会随机产生10个Github Page。用户可以使用这个功能探索未知的Github Page。更多信息Github Page搜索Jack Online

December 31, 2018 · 1 min · jiezi

NEL程序员专用轻钱包 进入0.01状态了

这个轻钱包能干什么,现在就能在测试网看个余额,转个帐,调用个合约。而且功能非常程序员化你会说是不是没啥用但是他有非常有用,因为他可以很容易的拼出NEOGUI拼不出来的交易比如参与ICO交易,其实用这就不是个事儿了。第一步,导入一个Key 进来这个钱包使用单KEY设计,一次只用一个KEY支持WIF NEP2 NEP6 导入我只演示一下NEP6 导入,其他都比这个简单。NEP6含有多个账户,选中你要使用的一个,输入密码,按GetKey成功的话,左上角会显示你的地址,右上角是高度,注意一下。这是一个轻钱包,我们尽量调用标准的NEO rpc指令,但还是有一些rpc没有的,最主要的utxo,rpc没有,所以找NEL的api要。目前只支持testnet,主网api上线后,可以用切换网络按钮切换主网测试网功能,查看余额导入key以后按刷新,就能看到自己的余额查看UTXO我们能看到我们的钱到底是如何组成的查看NEP5余额用Config NEP5 功能,加入Nep5合约地址你就能查看任何Nep5 余额,不需要告诉任何人你的Nep5发布了,你知道地址,这个钱包就能看记得要刷新一下啊,才能看到我有9千9百万NNS,NNS是我自己发的测试币,不用羡慕功能 UTXO转账用这个轻钱包进行UTXO转账非常清楚,可以搞各种幺蛾子首先确保make transaction选中,这就是手工构造交易功能。然后确保contractTransaction选中,这说明我们构造一个转账交易,另一个是智能合约交易,等下会说。设置输入灵魂功能,不同于其他钱包只让你转账,我们这里,你可以拖拽一个具体的UTXO进来,作为输入。多个也没问题,你可以自己决定具体从自己的哪几张零钱来转账。当然UTXO不懂你就会觉得很痛苦了。不懂UTXO自己补课,这里就不展开讲了。设置输出设置了输入以后,Outputs自动出现了,这是找零部分我们会自动把零钱给你找回来。现在没有设置,全部是零钱找回来了。这样也可以,这就是自己给自己转账。如果我们要给别人转账怎么办在outputs列表框里面 鼠标右键,用 add output设置转账目标,什么资产,多少,就ok如果你转的钱太多交易无法完成,我会用红色提示你你也可以选中不要的输出,右键删除他这是一个可用的转账,我向adzq….这个地址转了1个gas他看起来是合法的Witness是见证人我们识别出来需要一个地址签名见证人,这个一般转账都是用地址签名见证人,就是我们的key。这里会自动完成的,不用关心他。以后有些特殊脚本的特殊见证人功能还没开发^_^签名与发送交易签名就是把witness的内容填上去,然后广播交易看到txid=xxx就是交易成立了找个浏览器确认一下,交易确实成立了功能、智能合约调用选择InvocationTransaction就是发起智能合约交易目前只支持AppCall 智能合约,就是NEOGUI的调用合约功能PublishSC是发布合约,还未实现Custom是自定义合约,还未实现你可以拖一个Gas input 进来,然后全部找零给自己。你的合约fee<10时,neo执行合约时免费的。但是必须要有一个gas的input其实和NeoGUI的思路是一样的填脚本hash,找到智能合约,然后填参数。但是Neogui填参数太蛋疼了我们直接用一个json替代掉了他。点击ok以后,这里显示出了执行的代码显示了avm你也不会看对吧,所以直接显示了反汇编。测试合约和NEOGUI一样,必须先TestTest之后可以计算出网络feeState 是 halt break 表示合约正常执行不正常结束是fault签名发布交易,完事儿完事儿还可以再用NeonDebug 输入txid调试一把功能、其他你说nep5转账,刚才的智能合约调用就演示了一次nep5转账好么你说ico,只要在调用合约的同时拖一个neo input,然后对着脚本地址转几个neo就是ico了呀。以后有时间专门搞这个再说,这是给程序员用的,看源码,然后订制你自己的版本。仓库地址https://github.com/NewEconoLa…看地址就知道,其实这是我们做的轻钱包SDK,帮助你做轻钱包的请把这个轻钱包当作一个示例。我们还有ts版本的轻钱包SDK,帮助你们在网页里开发轻钱包。欢迎吐槽欢迎加入NEL,QQ群 377076520Ts版本的轻钱包sdk,部分功能已经在我们的一个测试网站中使用http://be.nel.group/原文转自:https://www.cnblogs.com/crazy…

December 29, 2018 · 1 min · jiezi

修复 github 项目的语言属性

issueLaravel 开源电商项目源码 被 github 判断认为是 HTML 项目,但是实际项目并没有 html 代码。这就尴尬了,只有默默的通过 google 搜索 github change project type 发现这篇文章:How to Change Repo Language in GitHub简单来说只要在项目根目录下添加 .gitattributes 文件,然后通过设置相关目录或者类型文件标记为相关语言即可。gitattributes支持的属性语法如下:linguist-documentationlinguist-languagelinguist-vendoredlinguist-generatedlinguist-detectableusage*.css linguist-vendored*.js linguist-language=Vuemodules/* linguist-language=PHPresourcesgithub/linguist

December 29, 2018 · 1 min · jiezi

gitlab 安装、配置

gitlab 安装、配置对于企业级的私有 git 仓库,gitlab 是个不错的选择。今天就来说说 gitlab 的安装、配置。系统配置建议:最低双核 4G 内存。当前针对 gitlab 版本:11.5.3。1. 说明根据官方的安装教程,选取 ubuntu 环境下的社区版进行安装:https://about.gitlab.com/install/#ubuntu?version=ce。如果需要其他的环境(如 CentOS、docker 等)或者企业版,参考这里:https://about.gitlab.com/install/。注:社区版是免费的,企业版是收费的。2. 安装、配置所需依赖sudo apt-get install -y curl openssh-server ca-certificatessudo apt-get install -y postfix3. 添加安装包地址,并安装curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bashsudo EXTERNAL_URL=“http://gitlab.example.com” apt-get install gitlab-ce这里需要把 http://gitlab.example.com 改成你自己的真正对外服务的 gitlab url 地址(也可以安装后在配置文件里面更改)。到这里为止,gitlab 就算安装好了。4. 安装之后gitlab 默认建议安装在一个单独的主机上,默认使用内置的 nginx 服务器,并使用 80 和 8080 两个端口。如果你是按照 gitlab 的默认建议,使用的是单独的主机,直接访问 ip 地址 http://ip,或者把域名解析到这台机器上后直接访问域名 http://gitlab.your.com,然后按照步骤在 web 页面上初始化 gitlab 就可以了。然而,很多情况下,gitlab 并不会部署到一台单独的服务器上,而是像其他很多服务一样(如 jenkins),部署到同一台服务器上,然后使用 nginx 反向代理。5. nginx 反向代理配置添加 nginx 配置:upstream gitlab-workhorse { server unix:/var/opt/gitlab/gitlab-workhorse/socket;}server { listen 0.0.0.0:80; listen [::]:80; server_name gitlab.your.com; server_tokens off; root /opt/gitlab/embedded/service/gitlab-rails/public; access_log /var/log/nginx/gitlab_access.log; error_log /var/log/nginx/gitlab_error.log; location / { client_max_body_size 0; gzip off; proxy_read_timeout 300; proxy_connect_timeout 300; proxy_redirect off; proxy_http_version 1.1; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://gitlab-workhorse; }}如果想要了解更详细的配置,可以参考:NGINX settings | GitLab。重新加载 nginx 配置:service nginx reload# orservice nginx restart修改 gitlab 配置:vi /etc/gitlab/gitlab.rbexternal_url ‘http://gitlab.your.com’ # 此处修为你自己的 gitlab urlweb_server[’external_users’] = [‘www-data’] # 设置外部 webserver 用户nginx[’enable’] = false # 不使用内置的 nginxsudo usermod -aG gitlab-www www-data # 把 www-data 用户添加到 gitlab-www 组 gitlab 服务默认使用的是 8080 端口,如果 8080 端口已经被其他程序(如 tomcat)占用,需要改成其他端口:unicorn[‘port’] = 8081 # 改成你自己觉得好的端口更新 gitlab 配置,重启服务:gitlab-ctl reconfiguregitlab-ctl restart现在你就可以访问 http://gitlab.your.com,然后按照步骤在 web 页面上初始化 gitlab 就可以了。6. 常用命令gitlab-ctl start # 启动 gitlabgitlab-ctl stop # 停止 gitlabgitlab-ctl restart # 重启 gitlabgitlab-ctl status # 查看服务状态vi /etc/gitlab/gitlab.rb # 修改配置文件gitlab-ctl reconfigure # 重新编译 gitlab 配置gitlab-rake gitlab:check SANITIZE=true –trace # 检查 gitlabgitlab-ctl tail # 查看日志gitlab-ctl tail nginx/gitlab_access.log7. 常用目录/var/log/gitlab/ # 日志地址 /var/opt/gitlab/ # 服务地址 8. 查看版本cat /opt/gitlab/embedded/service/gitlab-rails/VERSION9. 可能遇到的一些问题9.1 File to import not found or unreadable解决:需要 npm 安装一下cd /opt/gitlab/embedded/service/gitlab-railsnpm installgitlab-ctl restart # 重启服务后续更多博客,查看 https://github.com/senntyou/blogs作者:深予之 (@senntyou)版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证) ...

December 28, 2018 · 2 min · jiezi

Neo 虚拟机

上一篇《Neo 编译器》中说明了Neo编译器是怎么把CIL转成neo虚拟机的opcode,那么vm虚拟机又是怎么处理这些代码的,这篇文章我们看一下虚拟机的代码。框架虚拟机所处的位置在框架图中,我们可以看出Virtual Machine有以下作用读取Opcode(smart contract),在Execution Engine中执行Execution Engine可以进行逻辑运算Interop Service可以调用External Data系统调用(OP_SYSCALL)可以访问区块链账本的信息下面我们先看一下虚拟机如何读取Opcode。VM对象关系下面展示的图不是UML, UML太麻烦,还是脑图比较符合思维逻辑的发展。关键的几个对象Execution Engine:执行引擎Execution Context:执行上下文Stack Item:堆栈的一条数据Crypto:C#的加密库执行引擎IScriptTable里面存贮了AppCall命令可以调用的其他contract的代码,这一块需要研究一下区块链的实现,这个以后再仔细研究。InteropService专门用来响应SYSCALL,具体有哪些是系统调用,用来干什么的,后面通过例子再来说明。InvocationStack是调用栈,传入参数,调用其他合约都会有一个新的调用栈EvaluationStack是计算栈,用来执行操作AltStack是备用栈,计算栈算出的中间结果可以保存在备用栈执行上下文执行上下文 每个变量都蛮好理解的,重点是下面看看怎么用的。vm执行流程vm代码执行流程构造,此时可以传入script container,script table,后面我们看看在区块链上这些都是从哪里来的,这里只专注于vm的执行流程,暂且不深究了。加载.avm,avm是编译器编译出来的一串数字,通过engine.LoadScript可以加载。execute开始执行, 下面看一下代码 public void Execute() { State &= ~VMState.BREAK; while (!State.HasFlag(VMState.HALT) && !State.HasFlag(VMState.FAULT) && !State.HasFlag(VMState.BREAK)) StepInto(); } public void StepInto() { if (InvocationStack.Count == 0) State |= VMState.HALT; if (State.HasFlag(VMState.HALT) || State.HasFlag(VMState.FAULT)) return; OpCode opcode = CurrentContext.InstructionPointer >= CurrentContext.Script.Length ? OpCode.RET : (OpCode)CurrentContext.OpReader.ReadByte(); try { ExecuteOp(opcode, CurrentContext); } catch { State |= VMState.FAULT; } }看一下这行代码,OpCode opcode = CurrentContext.InstructionPointer >= CurrentContext.Script.Length ? OpCode.RET : (OpCode)CurrentContext.OpReader.ReadByte();代码执行完了以后,插入OpCode.RET如果不是RET,则read一个字节的opcodeExecuteOp函数就是具体的执行OpCode的语义,我们通过一个例子来说明具体的一个例子还是上次的那个代码using Neo.SmartContract.Framework;using Neo.SmartContract.Framework.Services.Neo;public class Sum : SmartContract{ public static int Main(int a, int b) { return a + b; }}测试虚拟机的代码using System;using System.IO;using System.Linq;using Neo;using Neo.VM;using Neo.Cryptography;namespace ConsoleApplication1{ class Program { static void Main(string[] args) { var engine = new ExecutionEngine(null, Crypto.Default); engine.LoadScript(File.ReadAllBytes(@“C:\……\Test1.avm”)); using (ScriptBuilder sb = new ScriptBuilder()) { sb.EmitPush(4); // 对应形参 b sb.EmitPush(3); // 对应形参 a engine.LoadScript(sb.ToArray()); } engine.Execute(); // 开始执行 var result = engine.EvaluationStack.Peek().GetBigInteger(); // 在这里设置返回值 Console.WriteLine($“执行结果 {result}”); Console.ReadLine(); } }}执行的具体过程生成的代码太长了,需要有点耐心才能看完,如果图片不清晰,可以去代码仓库下载pdf具体的执行过程总结文章只是过了一下一个简单的代码,后面我们需要研究一下系统调用和访问外部存贮,智能合约之间互相调用的情况。作者:沈寅原文链接:https://www.jianshu.com/p/b7a… ...

December 27, 2018 · 1 min · jiezi

Neo编译器

上一篇区块链研究方案先整理一下Neo编译器的知识吧。项目链接Neo本身是开源的,在github搜索就可以拿到源码,我为了方便调试,把所有代码都放在一起。https://github.com/benhaben/n…Neo是C#开发的,大部分代码可以跨平台,但是也有不能再mac上运行的代码,比如改装后的leveldb的代码,所以最好还是用VisualStudio在windows上调试研究较好。本文档主要是总结自己所研究的东西,现在不太规范,主要考虑好理解,并不完善,后面也可以进一步规范化提交到neo项目中。主框架框架大家可以看到Compiler所处的位置Compiler作用在Neo区块链系统中,智能合约是一段代码,可以完成一定的逻辑,最后算出合约的结果。现在已经有很多具体的应用了,感兴趣的可以看一下基于Neo做的项目。如果对比特币或者智能合约不了解,不知道为什么需要有这些代码,可以看一下比特币的白皮书。这里简单说一下,在比特币系统中,当一个“人”(可以看成一个公钥)需要和另一个“人”产生交易的时候,这段代码用来检查身份,分配比特币。具体的了解可以看一下普林斯顿的公开课下面进入到Neo compiler的介绍了,前面所需的基础知识本文不在关注。Compiler的框架下面的图主要显示代码的主要流程:基本的流程Neo可以用各种语言写,不过现在主要是C#。Neo的编译器主要是一个翻译器C#代码被C#编译器编译成MSIL,对MSIL的理解可以查看Standard ECMA-335 Common Language Infrastructure (CLI)Neo compiler使用Mono.Cecil读取ILNeo编译器只关注C#中的static function,所以只是C#语言的一个超级阉割版Neo的编译器遍历IL,根据语义转成Neo虚拟机的opcode至于MSIL向neo.vm的opcode怎么转,需要仔细研究neo.vm的opcode的设计Compiler工作一个具体事例先看一段智能合约代码这段代码没有什么实际的作用,就是返回a+b,但是main可以接受参数。using Neo.SmartContract.Framework;using Neo.SmartContract.Framework.Services.Neo;public class Sum : SmartContract{ public static int Main(int a, int b) { return a + b; }}MSILmain function IL codeIL_0000 NopIL_0001 Ldarg_0IL_0002 Ldarg_1IL_0003 AddIL_0004 Stloc_0IL_0005 Br_SIL_0007 Ldloc_0IL_0008 Ret这段代码很简单,就是读取参数Ldarg_0,Add,返回。可以看到CLR的虚拟机也是堆栈虚拟机。关于基于栈的虚拟机和基于寄存器的虚拟机可以看一下这些文章:栈式虚拟机和寄存器式虚拟机?另外还有一篇概念讲解的很详细的文章虚拟机随谈(一):解释器,树遍历解释器,基于栈与基于寄存器,大杂烩neo.compiler为了感性的认识neo编译器做了什么,我们可以看一下上面的只能合约被翻译成了什么hex:53-C5-6B-6C-76-6B-00-52-7A-C4-6C-76-6B-51-52-7A-C4-61-6C-76-6B-00-C3-6C-76-6B-51-C3-93-6C-76-6B-52-52-7A-C4-62-03-00-6C-76-6B-52-C3-61-6C-75-66实际上是一串数字了,每个数字对应一个vm的操作码或者是数值,为了更好理解,把汇编代码放出来PUSH4PUSH3RETPUSH3NEWARRAYTOTALSTACKFROMALSTACKDUPTOALTSTACKPUSH0PUSH2ROLLSETITEMFROMALSTACKDUPTOTALSTACKPUSH1PUSH2ROLLSETITEMNOPFROMALSTACKDUPTOTALSTACKPUSH0PICKITEMFROMALSTACKDUPTOTALSTACKPUSH1PICKITEMADDFROMALSTACKDUPTOTALSTACKPUSH2PUSH2ROLLSETITEMJMPFROMALSTACKDUPTOTALSTACKPUSH2PICKITEMNOPFROMALSTACKDROPretneo汇编的说明,可以查看这个文档我们可以发现如下情况:MSIL的代码很短,Neo.VM的代码很长,这是由于虚拟机的指令和能力不同造成的。我们只需要关注,汇编代码处理了局部变量的存贮获取,参数的传递,程序的退出,还有add指令。汇编代码和compiler的生成算法相关,需要我们去同时研究neo的编译器和虚拟机,才能明白具体的细节。具体每一行的含义,怎么执行的,可以查看这个文档后面还会有一个讲解虚拟机的文章,到那个时候在仔细说明neo.compiler代码阅读指南代码阅读还是很头痛的,所以做了两个脑图:compiler执行脑图compiler对象关系对象关系ILModule是对MSIL的一个映射,包含模块,类型,函数,字段,函数中又包含返回值,参数,函数体,可以点开脑图一层一层查看。Mono.Cecil是使用来读取MSIL的,他也是对MSIL的一个映射,由于没有文档,只能看代码知道他的类结构了,这一部分在脑图中没有显示,不过没关系,compiler会把感兴趣的代码转成ILModuleModuleConverter用来遍历ILModule,把里面的MSIL转成Neo.VM的代码,存贮在NeoModule具体两边的指令如何对应,真是需要一个一个理解的,非常繁琐,两边都有很多的指令。可以看看这个代码总结看完这个文章,并不能了解到具体的细节,具体的细节已经在代码中,这篇文章的主要目的是提供很多资料,提供大的框架,帮助对Neo.Compiler感兴趣的程序员加速阅读代码的速度。作者:沈寅原文链接:https://www.jianshu.com/p/646…

December 27, 2018 · 1 min · jiezi

看动画轻松理解「递归」与「动态规划」

在学习「数据结构和算法」的过程中,因为人习惯了平铺直叙的思维方式,所以「递归」与「动态规划」这种带循环概念(绕来绕去)的往往是相对比较难以理解的两个抽象知识点。程序员小吴打算使用动画的形式来帮助理解「递归」,然后通过「递归」的概念延伸至理解「动态规划」算法思想。什么是递归先下定义:递归算法是一种直接或者间接调用自身函数或者方法的算法。通俗来说,递归算法的实质是把问题分解成规模缩小的同类问题的子问题,然后递归调用方法来表示问题的解。它有如下特点:一个问题的解可以分解为几个子问题的解这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一样存在递归终止条件,即必须有一个明确的递归结束条件,称之为递归出口通过动画一个一个特点来进行分析。1.一个问题的解可以分解为几个子问题的解子问题就是相对与其前面的问题数据规模更小的问题。在动图中①号问题(一块大区域)划分为②号问题,②号问题由两个子问题(两块中区域)组成。2. 这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一样「①号划分为②号」与「②号划分为③号」的逻辑是一致的,求解思路是一样的。3. 存在递归终止条件,即存在递归出口把问题分解为子问题,把子问题再分解为子子问题,一层一层分解下去,不能存在无限循环,这就需要有终止条件。①号划分为②号,②号划分为③号,③号划分为④号,划分到④号的时候每个区域只有一个不能划分的问题,这就表明存在递归终止条件。从递归的经典示例开始一.数组求和Sum(arr[0…n-1]) = arr[0] + Sum(arr[1…n-1])后面的 Sum 函数要解决的就是比前一个 Sum 更小的同一问题。Sum(arr[1…n-1]) = arr[1] + Sum(arr[2…n-1])以此类推,直到对一个空数组求和,空数组和为 0 ,此时变成了最基本的问题。Sum(arr[n-1…n-1] ) = arr[n-1] + Sum([])二.汉诺塔问题汉诺塔(Hanoi Tower)问题也是一个经典的递归问题,该问题描述如下:汉诺塔问题:古代有一个梵塔,塔内有三个座A、B、C,A座上有64个盘子,盘子大小不等,大的在下,小的在上。有一个和尚想把这个盘子从A座移到B座,但每次只能允许移动一个盘子,并且在移动过程中,3个座上的盘子始终保持大盘在下,小盘在上。① 如果只有 1 个盘子,则不需要利用 B 塔,直接将盘子从 A 移动到 C 。② 如果有 2 个盘子,可以先将盘子 2 上的盘子 1 移动到 B ;将盘子 2 移动到 C ;将盘子 1 移动到 C 。这说明了:可以借助 B 将 2 个盘子从 A 移动到 C ,当然,也可以借助 C 将 2 个盘子从 A 移动到 B 。③ 如果有 3 个盘子,那么根据 2 个盘子的结论,可以借助 C 将盘子 3 上的两个盘子从 A 移动到 B ;将盘子 3 从 A 移动到 C ,A 变成空座;借助 A 座,将 B 上的两个盘子移动到 C 。 ④ 以此类推,上述的思路可以一直扩展到 n 个盘子的情况,将将较小的 n-1个盘子看做一个整体,也就是我们要求的子问题,以借助 B 塔为例,可以借助空塔 B 将盘子A上面的 n-1 个盘子从 A 移动到 B ;将A 最大的盘子移动到 C , A 变成空塔;借助空塔 A ,将 B 塔上的 n-2 个盘子移动到 A,将 C 最大的盘子移动到 C, B 变成空塔。。。三.爬台阶问题问题描述:一个人爬楼梯,每次只能爬1个或2个台阶,假设有n个台阶,那么这个人有多少种不同的爬楼梯方法?先从简单的开始,以 4 个台阶为例,可以通过每次爬 1 个台阶爬完楼梯:可以通过先爬 2 个台阶,剩下的每次爬 1 个台阶爬完楼梯在这里,可以思考一下:可以根据第一步的走法把所有走法分为两类:① 第一类是第一步走了 1 个台阶② 第二类是第一步走了 2 个台阶所以 n 个台阶的走法就等于先走 1 阶后,n-1 个台阶的走法 ,然后加上先走 2 阶后,n-2 个台阶的走法。用公式表示就是: f(n) = f(n-1)+f(n-2) 有了递推公式,递归代码基本上就完成了一半。那么接下来考虑递归终止条件。当有一个台阶时,我们不需要再继续递归,就只有一种走法。所以 f(1)=1。通过用 n = 2,n = 3 这样比较小的数试验一下后发现这个递归终止条件还不足够。n = 2 时,f(2) = f(1) + f(0)。如果递归终止条件只有一个 f(1) = 1,那 f(2) 就无法求解,递归无法结束。 所以除了 f(1) = 1 这一个递归终止条件外,还要有 f(0) = 1,表示走 0 个台阶有一种走法,从思维上以及动图上来看,这显得的有点不符合逻辑。所以为了便于理解,把 f(2) = 2 作为一种终止条件,表示走 2 个台阶,有两种走法,一步走完或者分两步来走。总结如下:① 假设只有一个台阶,那么只有一种走法,那就是爬 1 个台阶② 假设有两个个台阶,那么有两种走法,一步走完或者分两步来走通过递归条件:f(1) = 1;f(2) = 2;f(n) = f(n-1)+f(n-2)很容易推导出递归代码:int f(int n) { if (n == 1) return 1; if (n == 2) return 2; return f(n-1) + f(n-2);}通过上述三个示例,总结一下如何写递归代码:1.找到如何将大问题分解为小问题的规律2.通过规律写出递推公式3.通过递归公式的临界点推敲出终止条件4.将递推公式和终止条件翻译成代码什么是动态规划介绍动态规划之前先介绍一下分治策略(Divide and Conquer)。分治策略将原问题分解为若干个规模较小但类似于原问题的子问题(Divide),「递归」的求解这些子问题(Conquer),然后再合并这些子问题的解来建立原问题的解。因为在求解大问题时,需要递归的求小问题,因此一般用「递归」的方法实现,即自顶向下。动态规划(Dynamic Programming)动态规划其实和分治策略是类似的,也是将一个原问题分解为若干个规模较小的子问题,递归的求解这些子问题,然后合并子问题的解得到原问题的解。 区别在于这些子问题会有重叠,一个子问题在求解后,可能会再次求解,于是我们想到将这些子问题的解存储起来,当下次再次求解这个子问题时,直接拿过来就是。 其实就是说,动态规划所解决的问题是分治策略所解决问题的一个子集,只是这个子集更适合用动态规划来解决从而得到更小的运行时间。 即用动态规划能解决的问题分治策略肯定能解决,只是运行时间长了。因此,分治策略一般用来解决子问题相互对立的问题,称为标准分治,而动态规划用来解决子问题重叠的问题。与「分治策略」「动态规划」概念接近的还有「贪心算法」「回溯算法」,由于篇幅限制,程序员小吴就不在这进行展开,在后续的文章中将分别详细的介绍「贪心算法」、「回溯算法」、「分治算法」,敬请关注:)将「动态规划」的概念关键点抽离出来描述就是这样的:1.动态规划法试图只解决每个子问题一次2.一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。从递归到动态规划还是以 爬台阶 为例,如果以递归的方式解决的话,那么这种方法的时间复杂度为O(2^n),具体的计算可以查看笔者之前的文章 《冰与火之歌:时间复杂度与空间复杂度》。相同颜色代表着 爬台阶问题 在递归计算过程中重复计算的部分。通过图片可以发现一个现象,我们是 自顶向下 的进行递归运算,比如:f(n) 是f(n-1)与f(n-2)相加,f(n-1) 是f(n-2)与f(n-3)相加。思考一下:如果反过来,采取自底向上,用迭代的方式进行推导会怎么样了?下面通过表格来解释 f(n)自底向上的求解过程。台阶数123456789走法数12 表格的第一行代表了楼梯台阶的数目,第二行代表了若干台阶对应的走法数。其中f(1) = 1 和 f(2) = 2是前面明确的结果。第一次迭代,如果台阶数为 3 ,那么走法数为 3 ,通过 f(3) = f(2) + f(1)得来。台阶数123456789走法数123 第二次迭代,如果台阶数为 4 ,那么走法数为 5 ,通过 f(4) = f(3) + f(2)得来。台阶数123456789走法数1235 由此可见,每一次迭代过程中,只需要保留之前的两个状态,就可以推到出新的状态。show me the codeint f(int n) { if (n == 1) return 1; if (n == 2) return 2; // a 保存倒数第二个子状态数据,b 保存倒数第一个子状态数据, temp 保存当前状态的数据 int a = 1, b = 2; int temp = a + b; for (int i = 3; i <= n; i++) { temp = a + b; a = b; b = temp; } return temp; }程序从 i = 3 开始迭代,一直到 i = n 结束。每一次迭代,都会计算出多一级台阶的走法数量。迭代过程中只需保留两个临时变量 a 和 b ,分别代表了上一次和上上次迭代的结果。为了便于理解,引入了temp变量。temp代表了当前迭代的结果值。看一看出,事实上并没有增加太多的代码,只是简单的进行了优化,时间复杂度便就降为O(n),而空间复杂度也变为O(1),这,就是「动态规划」的强大!详解动态规划「动态规划」中包含三个重要的概念:【最优子结构】【边界】【状态转移公式】在「 爬台阶问题 」中f(10) = f(9) + f(8) 是【最优子结构】 f(1) 与 f(2) 是【边界】 f(n) = f(n-1) + f(n-2) 【状态转移公式】「 爬台阶问题 」 只是动态规划中相对简单的问题,因为它只有一个变化维度,如果涉及多个维度的话,那么问题就变得复杂多了。 难点就在于找出 「动态规划」中的这三个概念。比如「 国王和金矿问题 」。国王和金矿问题有一个国家发现了 5 座金矿,每座金矿的黄金储量不同,需要参与挖掘的工人数也不同。参与挖矿工人的总数是 10 人。每座金矿要么全挖,要么不挖,不能派出一半人挖取一半金矿。要求用程序求解出,要想得到尽可能多的黄金,应该选择挖取哪几座金矿?找出 「动态规划」中的这三个概念国王和金矿问题中的【最优子结构】国王和金矿问题中的【最优子结构】有两个:① 4 金矿 10 工人的最优选择② 4 金矿 (10 - 5) 工人的最优选择4 金矿的最优选择与 5 金矿的最优选择之间的关系是MAX[(4 金矿 10 工人的挖金数量),(4 金矿 5 工人的挖金数量 + 第 5 座金矿的挖金数量)]国王和金矿问题中的【边界】国王和金矿问题中的【边界】 有两个:① 当只有 1 座金矿时,只能挖这座唯一的金矿,得到的黄金数量为该金矿的数量② 当给定的工人数量不够挖 1 座金矿时,获取的黄金数量为 0国王和金矿问题中的【状态转移公式】我们把金矿数量设为 N,工人数设为 W,金矿的黄金量设为数组G[],金矿的用工量设为数组P[],得到【状态转移公式】:边界值:F(n,w) = 0 (n <= 1, w < p[0])F(n,w) = g[0] (n==1, w >= p[0])F(n,w) = F(n-1,w) (n > 1, w < p[n-1])F(n,w) = max(F(n-1,w), F(n-1,w-p[n-1]) + g[n-1]) (n > 1, w >= p[n-1])国王和金矿问题中的【实现】先通过几幅动画来理解 「工人」 与 「金矿」 搭配的方式1.只挖第一座金矿在只挖第一座金矿前面两个工人挖矿收益为 零,当有三个工人时,才开始产生收益为 200,而后即使增加再多的工人收益不变,因为只有一座金矿可挖。2.挖第一座与第二座金矿在第一座与第二座金矿这种情况中,前面两个工人挖矿收益为 零,因为 W < 3,所以F(N,W) = F(N-1,W) = 0。 当有 三 个工人时,将其安排挖第 一 个金矿,开始产生收益为 200。当有 四 个工人时,挖矿位置变化,将其安排挖第 二 个金矿,开始产生收益为 300。当有 五、六 个工人时,由于多于 四 个工人的人数不足以去开挖第 一 座矿,因此收益还是为 300。当有 七 个工人时,可以同时开采第 一 个和第 二 个金矿,开始产生收益为 500。3.挖前三座金矿这是「国王和金矿」 问题中最重要的一个动画之一,可以多看几遍 4.挖前四座金矿这是「国王和金矿」 问题中最重要的一个动画之一,可以多看几遍 国王和金矿问题中的【规律】仔细观察上面的几组动画可以发现:对比「挖第一座与第二座金矿」和「挖前三座金矿」,在「挖前三座金矿」中,3 金矿 7 工人的挖矿收益,来自于 2 金矿 7 工人和 2 金矿 4 工人的结果,Max(500,300 + 350) = 650;对比「挖前三座金矿」和「挖前四座金矿」,在「挖前四座金矿」中,4 金矿 10 工人的挖矿收益,来自于 3 金矿 10 工人和 3 金矿 5 工人的结果,Max(850,400 + 300) = 850;国王和金矿问题中的【动态规划代码】代码来源:https://www.cnblogs.com/SDJL/archive/2008/08/22/1274312.html//maxGold[i][j] 保存了i个人挖前j个金矿能够得到的最大金子数,等于 -1 时表示未知int maxGold[max_people][max_n];int GetMaxGold(int people, int mineNum){ int retMaxGold; //声明返回的最大金矿数量 //如果这个问题曾经计算过 if(maxGold[people][mineNum] != -1){ retMaxGold = maxGold[people][mineNum]; //获得保存起来的值 }else if(mineNum == 0) { //如果仅有一个金矿时 [ 对应动态规划中的"边界"] if(people >= peopleNeed[mineNum]) //当给出的人数足够开采这座金矿 retMaxGold = gold[mineNum]; //得到的最大值就是这座金矿的金子数 else //否则这唯一的一座金矿也不能开采 retMaxGold = 0; //得到的最大值为 0 个金子 }else if(people >= peopleNeed[mineNum]) // 如果人够开采这座金矿[对应动态规划中的"最优子结构"] { //考虑开采与不开采两种情况,取最大值 retMaxGold = max( GetMaxGold(people - peopleNeed[mineNum],mineNum - 1) + gold[mineNum], GetMaxGold(people,mineNum - 1) ); }else//否则给出的人不够开采这座金矿 [ 对应动态规划中的"最优子结构"] { retMaxGold = GetMaxGold(people,mineNum - 1); //仅考虑不开采的情况 maxGold[people][mineNum] = retMaxGold; } return retMaxGold;}希望通过这篇文章,大家能对「递归」与「动态规划」有一定的理解。后续将以「动态规划」为基础研究多重背包算法、迪杰特斯拉算法等更高深的算法问题,同时「递归」的更多概念也会在「分治算法」章节再次延伸,敬请对程序员小吴保持关注:) ...

December 27, 2018 · 3 min · jiezi

游戏 & Github Page

snakewizard.github.io贪吃蛇小游戏2. mattbasile.github.io龙珠 DragonballZ-Battle3. nathandhyou.github.io2048 online4. triciasykes.github.ioBlackJack game5. cmh5.github.ioPet Funny Game6. diegorodriguezgzz.github.ioDestreza Duo7. markokerkez.github.io石头 布 剪刀 游戏Rock Paper Scissors Game8. NamedRandom.github.ioKittens GameKittens Game是一个村庄模拟文本游戏。你管理一个小猫村,因为他们获得资源和解锁新技术。 这是一个古老的学校游戏,是多年的爱和辛勤劳动的产物。你可以在Option里面选择中文,需要自己添加翻译。9. andregregorio.github.ioMemory游戏规则:查找相同8张相同卡的最小数量;每场比赛有两次尝试;在玩家发现两张相似且连续的牌时,他将获得一分;当玩家进行8次点击(点数)时,游戏将显示玩家表现的总结;玩家仍然可以获得星星,其中包括:5星最多12次移动,4星级最多16次移动,3星级最多20次移动,2星级最多24次移动,1星级最多28次移动,29次移动零星级。10. pavuloff.github.io不错的游戏,像素风格。总结探索更多Github Page上的小游戏,点击下面的Github Page Explore - Game链接。更多信息Github Page Explore - GameGithub Page 搜索Share Code - 我的个人网站

December 26, 2018 · 1 min · jiezi

Github Page搜索工具更新 - 探索功能

探索功能提供了一种快速访问有意思的Github Page的途径,每周探索功能会更新有趣的搜索词条,你可以点击感兴趣的词条来获取该词条对应的Github Page。首批Github Page探索词条包括:GameOJ (Online Judge)VueCSS (Cascading Style Sheets)Resume

December 26, 2018 · 1 min · jiezi

NEO智能合约调试流程说明

Neo智能合约调试比较麻烦,我们NEL在开发NNS的过程中,发现现存的工具根本无法支撑我们开发如此复杂的智能合约。所以我们搞了一整套自己的智能合约调试工具第一步、编译智能合约安装neondebugGit 抓取编译:https://github.com/NewEconoLa…你将得到一个崭新的neon.exe用他替换掉你原来用的那个neon.exe怎么替换呢,请参照如下步骤移除你原来neon.exe 的path,换上neondebug的为什么要安装neondebugNeondebug 多导出了一个map.json文件,实现avm到源码的映射。同时按照hash 将 avm abi cs map.json 整理到一起比如生成就会直接用scripthash整理相关文件检查编译结果用neondebuggui “ load avm from file “ 按钮,加载刚生成的avm文件可以进行查看,查看以后,就可以用NEONDEBUGGUI的调试交易工具调试使用此脚本的交易。查看一次即可。文件会被copy到neondebuggui的目录中开源智能合约使用upload this to server 按钮,可以将智能合约代码上传到服务器,则任何人可以获取到此合约信息使用 load from server 按钮,可以根据scripthash查看服务器上的合约信息。第二步、产生一个交易安装neo-gui-nelGit抓取编译:https://github.com/NewEconoLa…你将得到一个NEL定制版本的NEO-GUI这个定制版本乍看没有什么东西,但是来发布一下刚才的智能合约你就会发现一点不同请注意neo-gui-nel 默认配置在testnet上面。发布合约NEL定制版本NEO-GUI已经率先支持了发布NEP4( dyncall)合约第三步、调试调用合约调用合约NEOGUI定制版也可以添加一个Array试想这个智能合约Main(string,object[] args),其中args[0] 是 一个 string[]这就需要array里面嵌套array,现在的neogui 是没有办法调用这样的合约的调试试运行合约当你点击试运行以后就已经可以调试了按照Neo-gui-nel默认的配置,你点击试运行之后,就会在这个目录得到一个0x00文件使用它就可以完成NEO智能合约调试了。使用NEONDEBUGGUI的Debug Transaction 选项卡LoadFromFile button,打开这个0x00文件你就得到了一个拥有智能合约详细执行的每一步的工具。并且可以对应到源码,可以观察执行栈细节,可以观察每一个数据的细节的工具。让你可以完全了解到这次test是怎么运行的调试交易发送一笔交易,等一会儿,等这笔交易被确认你就可以在fulllog目录找到这笔交易对应的文件打开它,就能够调试这笔交易在链上的实际运行细节。第四步、更多更方便只要有人upload过一次的智能合约,所有人随时都能调试时看到源码我们有一个爬虫,随时将testnet的合约的log文件上传到服务器即使你没有安装NEO-GUI-NEL,你使用loadformserver按钮也可以调试,只要你知道交易id就行。未来我们将开发纯网页版的调试工具未来我们也将同步主网的数据也上传服务器原文转自:https://mp.weixin.qq.com/s/sZ…

December 26, 2018 · 1 min · jiezi

markdown介绍及使用

了解及介绍markdown 今天忽然发现 markdown 是如此的强大,特别适合今后自己写技术博客,比如segmentfault、GitHub等技术社区参考以下博客:印象笔记终于支持 Markdown了,如何才能真正用好它?Markdown基本语法使用markdown参考以下使用教程:有道云笔记Markdown指南

December 26, 2018 · 1 min · jiezi

NEO插件钱包方案演示——安全与便捷的艺术统一

是不是总是被GUI钱包的同步惹恼?是不是担心网页钱包会拿走你的私钥?是不是想开发Web Dapp又感觉实现钱包签名操作很无力、无奈?没错!一个插件钱包就能解决以上所有问题。插件钱包是什么?是一个能够不用同步就能立即使用的钱包。是一个完全受你掌控的钱包,除了你自己谁都不能偷走你的私钥。是一个能够和web页面通信的钱包,你可以只专注你自己的业务逻辑。插件钱包的运行机制是什么?(以下以exe签名模式为例)Web页面构造指定的元素(如id为listAddrOut的select用于接收当前钱包的地址组)插件前端contentscript.js注入web页面,在打开页面时请求插件钱包的background.js插件钱包的background.js向exe签名程序发送交易信息(如一个转账,一个合约调用)exe签名程序获取交易信息,将信息展示给用户用户确认交易信息,在exe输入钱包密码,执行交易exe返回txid到插件钱包的background.js插件钱包的background.js返回txid到web页面插件钱包有几种签名模式?插件调用exe签名(如果你还不太相信插件,可以用这种)插件内js签名(如果你觉得装签名exe很麻烦,可以用这种)Neodun等硬件钱包签名(如果你觉得PC对于黑客来说就是不带门的,可以用这种)插件钱包如何保证我的安全?NEP6钱包文件保存在插件内部。插件的页面是运行在本地浏览器内的,不与其他任何第三方的服务器相关联。每次交易都在用户确认交易信息后,用户输入钱包密码后才实际执行。所有交易的构造在钱包内部完成,防止签名请求方提供与实际交易不符的显示信息。所有处理代码都是用户端代码(本地浏览器内js、exe),能够篡改或窃取数据的只有用户本机这仅仅是一个方案吗?NO!Github:https://github.com/NewEconoLa…NEL插件钱包(技术预览<ChouLou>版)PS:这里的余额通过链上UTXO数据计算得到,不需要解密钱包NNS域名转账与签名确认返回TXID查看这笔TX转账成功原文转自NEL新经济实验室:https://mp.weixin.qq.com/s/XU…

December 26, 2018 · 1 min · jiezi

十个有意思的Github Page

Cooolis.github.ioCooolis是一个操作系统命令技巧备忘录2. rfrd-tw.github.io2018 台灣公投視覺化3. confpad.github.ioConfPad:社区策划的技术会议讲座,视频,幻灯片等列表4. mazentouati.github.io个人网站This is the website’s repository for snacks console.点开之后才会发现这个网站有意思的地方。5. 1Crazy.github.io这个网站动态背景做的不错。6. wufengs.github.io网页头部滚动页面动态效果不错。7. rlcurriculum.github.io加强学习的自动化课程学习(Automated Curriculum Learning for Reinforcement Learning)8.wwcod.github.ioFaceGround - 搞笑的面部聊天!9. displayli.github.io创意设计10. achievement-hunter.github.io有意思的小游戏网站。Achievement Hunter GOTYGithub Page搜索工具Share Code - 我的个人网站

December 25, 2018 · 1 min · jiezi

NEO共识节点推荐搭建步骤

目录1 共识节点推荐搭建步骤1.1 远程访问1.2 硬件1.31.3 密码2 SSH认证密钥3 服务供应商具体配置4 Linux服务器配置4.1 使用su时4.2 以管理员身份登录时4.2.1 防火墙4.2.2 自动安全更新4.2.3 fail2ban4.2.4 双因素认证4.2.5 监控4.2.6 登录成功邮件通知4.2.7 Logwatch4.2.8 黑名单USB和防火墙存储4.2.9 共识节点安装与运行4.3 其他安全推荐共识节点推荐搭建步骤远程访问机器的远程访问仅限使用公钥与Yubico4/NEO实体公钥进行SSH认证。硬件出于安全考虑,节点不应在共享的机器上运行,而应在Deltalis, Equinix及A1 Arsenal等安全性较高的数据中心通过托管的方式运行。下列laaS供应商硬件配置可作为2018年的理想配置参考。因为共识节点有计算功能,因此应定期检查网络硬件要求以保证最优性能。如果无法进行托管,推荐laaS供应商与最低配置如下:OVH EG-32CPU: Intel Xeon E3-1270v6 – 4c/8t – 3.8GHzRAM: 32GB DDR4 ECC 2133 MHzSSD: softraid-1 2x450GB NVMeNET: 1 GbpsPacket WorkhorseCPU: Intel® Xeon E3-1240v5 – 4c/8t – 3.5GHzRAM: 32 GB DDR3 ECC 1333 MHzSSD: softraid-1 2x120GB Enterprise SSDNET: 2 x 1 Gbps BondedLiquidwebCPU: Intel Xeon E3-1275v6 – 4c/8t – 3.8GHzRAM: 32 GB DDR4 ECCSSD: softraid-1 2x240GB Enterprise SSDNET: 1 Gbps密码使用密码管理工具存放本次搭建过程中所需的每个密码(推荐使用Lastpass和Dashlane),所有服务均应启用双因素认证和实体密钥(如有)。所有密码均应设置高强度密码(使用此 lastpass方案)。SSH认证密钥仅限使用公钥访问SSH认证可起到密钥保护的作用,所以我们要求使用实体OpenPGP智能卡进行SSH认证。我们推荐使用Ubikey 4。有关Yubikey PGP的更多性能请参考官方文档.完整Windows指南完整Linux/MacOS指南更改默认管理员PIN码 12345678 与PIN码 123456 为可记忆的安全密码。将您的私钥添加到智能卡激活的认证代理服务后,gpg-agent就会与gpg2绑定——这是我们推荐的步骤。服务供应商具体配置打开供应商防火墙(不在OS设置中),设置为屏蔽全部,端口22、20333与10333除外。若有其他服务共享同一账户,请务必确保将节点放置在反关联性群组中。Linux服务器配置需给每个节点在两个辖区分别配置2个管理员。每个管理员都应有一个可以登录系统的专属用户和一个(SSH无权访问的)第三方共识用户,并且仅可通过该第三方共识用户访问共识节点的私钥(注意本指南使用的Ubuntu版本是16.04 LTS)。首次登录时,设置一个强效根密码,但仅在遗失sudo密码(或进行撤销操作)时才需使用根密码;su (sudo su on Ubuntu)passwd仍在使用su时Ubuntu更新包:apt-get updateapt-get upgradeCentOS:yum update添加管理员用户(两个管理员重复操作):useradd -m nodemkdir /home/node/.sshchmod 700 /home/node/.ssh本指南是基于bash编制的,所以在shell下拉列表中将bash设为偏好值:usermod -s /bin/bash node从管理员的 ssh-add -L中将Yubikey公钥复制到authorized_keys:vim /home/node/.ssh/authorized_keys许可设置:chmod 400 /home/node/.ssh/authorized_keyschown node:node /home/node -R设置管理员密码(这就是sudo密码):passwd node接下来我们就可以设置管理员的sudo了,添加 %sudo 群组,注释非 root的任何其他群组时使用#:visudo文件格式如下:# This file MUST be edited with the ‘visudo’ command as root.## Please consider adding local content in /etc/sudoers.d/ instead of# directly modifying this file.## See the man page for details on how to write a sudoers file.#Defaults env_resetDefaults mail_badpassDefaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"# Host alias specification# User alias specification# Cmnd alias specification# User privilege specificationroot ALL=(ALL:ALL) ALL# Members of the admin group may gain root privileges#%admin ALL=(ALL) ALL# Allow members of group sudo to execute any command%sudo ALL=(ALL:ALL) ALL# See sudoers(5) for more information on “#include” directives:#includedir /etc/sudoers.d将管理员添加到sudo群组:usermod -aG sudo node需登录并登出管理员账户后才能完成更新:su -l nodeexit再回到su,现在设置为仅可通过公钥与管理员登录SSH:vim /etc/ssh/sshd_config应添加下列行或在原有的基础上修改成如下形式,在AllowUsers中添加所有管理员并用空格键隔开:X11Forwarding noPermitRootLogin noPasswordAuthentication noAllowUsers nodeLoginGraceTime 30AllowTcpForwarding noTCPKeepAlive noAllowAgentForwarding noDebianBanner noBanner /etc/ssh/sshd-banner设置SSH的法律声明:echo “WARNING: Unauthorized access to this system is forbidden and will beprosecuted by law. By accessing this system, you agree that your actionsmay be monitored if unauthorized usage is suspected.” >> /etc/ssh/sshd-banner添加用户运行共识节点:useradd consensusmkdir /home/consensuschown consensus:consensus /home/consensus -R为共识节点创建一个非常强效的密码,应可安全地被两个管理员共享:passwd consensussu的最后一步就是以管理员身份登录后重启SSH。sudo systemctl restart sshd.service以管理员身份登录时防火墙首先在Debian(Ubuntu)中安装防火墙并锁定节点:sudo apt-get install ufwCentOS:sudo yum install epel-releasesudo yum install ufw在vim /etc/default/ufw中将IPV6设为yes,并设置为仅允许使用端口:sudo ufw default deny incomingsudo ufw allow sshsudo ufw allow 10333sudo ufw allow 20333sudo ufw disablesudo ufw enable自动安全更新Ubuntu:sudo apt-get install unattended-upgradessudo vim /etc/apt/apt.conf.d/10periodic更新以匹配:APT::Periodic::Update-Package-Lists “1”;APT::Periodic::Download-Upgradeable-Packages “1”;APT::Periodic::AutocleanInterval “7”;APT::Periodic::Unattended-Upgrade “1”;禁止不安全的自动更新:sudo vim /etc/apt/apt.conf.d/50unattended-upgrades更新,未注释的行有且仅有以下几行:Unattended-Upgrade::Allowed-Origins { “${distro_id}:${distro_codename}”; “${distro_id}:${distro_codename}-security”; “${distro_id}ESM:${distro_codename}”;};CentOS:安装yum-cron并允许安全包更新:sudo yum -y install yum-cronsudo systemctl start yum-cronsudo systemctl enable yum-cronsudo nano /etc/yum/yum-cron.conf更改设置以匹配:update_cmd = securityapply_updates = yesemit_via = emailemail_to = YOUR_EMAIL_TO_RECEIVE_UPDATE_NOTIFICATIONS有更新时,使用email_to功能插入你想发送提醒的邮件地址。fail2ban接下来我们就来安装fail2ban,这个工具可禁止防火墙上的可疑IP。该工具的默认值就可使用,因此简单的安装就够了。Ubuntu:sudo apt-get install fail2banCentOS:sudo yum install fail2ban双因素认证将2FA与SSH的实体OpenPGP密钥相结合是强效认证设置。在Ubuntu中使用下列代码安装:sudo apt-get install libpam-google-authenticatorCentOS (启用epel——同上):sudo yum install google-authenticator安装完毕后,以管理员身份运行指令时遵循每步指令,(回答y/y/y/n/y),先以管理员身份(两个)进行这步操作,完成后再更新PAM获得2FA:google-authenticator接下来编辑SSH配置来获得2FA设置许可:sudo vim /etc/pam.d/sshd在文件结尾添加以下行:auth required pam_google_authenticator.so在Ubuntu中放入密码提示的注释行:# Standard Un*x authentication.#@include common-authCentOS:#auth substack password-auth编辑sshd_config文件要求进行2FA认证:sudo vim /etc/ssh/sshd_config编辑文件允许认证,并添加认证方法行:ChallengeResponseAuthentication yesAuthenticationMethods publickey,password publickey,keyboard-interactive重启SSHD服务:sudo systemctl restart sshd.service在保持当前SSH窗口运行的同时打开另一个窗口,并登录以确认设置能正确运行。监视保持对共识节点的监视对于发现问题及改善NEO项目而言是至关重要的。我们仅会通过SSH通道安装并访问网络数据库(保留防火墙拦截设置)。安装预购建静态版本(以减少攻击面并杜绝不必要的依赖性):bash <(curl -Ss https://my-netdata.io/kickstart-static64.sh)访问方法是创建SSH通道并打开浏览器访问localhost:19999:ssh -f node@SERVERIP -L 19999:SERVERIP:19999 -N登录成功邮件通知sudo apt-get install mailutilsCentOS:sudo yum install mailx编辑默认bash档案:sudo vim /etc/profile在文件结尾添加以下行,编辑邮箱使其可接收登录通知:SIP="$(echo $SSH_CONNECTION | cut -d " " -f 1)“SHOSTNAME=$(hostname)SNOW=$(date +"%e %b %Y, %a %r”)echo ‘Someone from ‘$SIP’ logged into ‘$SHOSTNAME’ on ‘$SNOW’.’ | mail -s ‘SSH Login Notification’ ‘YOUR@EMAIL.HERE’Logwatch在Ubuntu上配置logwatch以发送节点的日常活动总结(通常无活动):sudo apt-get install logwatchCentOS:sudo yum install logwatch现在添加cron job将总结发送到你的邮箱:sudo vim /etc/cron.daily/00logwatch将默认执行命令变更为:/usr/sbin/logwatch –output mail –mailto YOUR@EMAIL.HERE –detail high黑名单USB与防火墙存储我们会把不需要的模块放入黑名单以减少攻击面,WiFi和蓝牙通常已经与服务器内核切断了(需验证!),因此仅需关闭USB存储。sudo vi /etc/modprobe.d/blacklist.conf添加以下行:blacklist usb-storageblacklist firewire-core安装并运行共识节点在Unbuntu上安装前提条件:sudo apt-get install unzip sqlite3 libsqlite3-dev libleveldb-dev libunwind-devCentOS:sudo yum install unzip leveldb-devel libunwind-devel以共识节点用户的身份登录:su consensuscd ~在发行版中下载、验证校验和、解压最新版neo-cli客户端:wget https://github.com/neo-project/neo-cli/releases/download/v2.5.2/neo-cli-YOURDISTRIBUTION.zipsha256sum neo-cli-YOURDISTRIBUTION.zipunzip neo-cli-YOURDISTRIBUTION.zipcd neo-clichmod u+x neo-cli复制节点运行的设置(测试网或主网):mv protocol.json protocol.json.backcp protocol.testnet.json protocol.json如果这是首次操作,你需要给共识节点创建钱包:./neo-clineo> create wallet /home/consensus/cn_wallet.jsonpassword: SOMESTRONGPASSWORDpassword: SOMESTRONGPASSWORD复制start_consensus脚本:cd ~wget https://raw.githubusercontent.com/CityOfZion/standards/master/assets/nodes/start_consensus.shchmod u+x start_consensus.sh编辑目录使其与你的钱包文件地址及密码匹配。现在就可以在supervisord的控制下在Ubuntu上运行了:sudo apt-get install supervisorCentOS:sudo yum install supervisor配置supervisord以执行start_consensus(在需要的情况下编辑文件):wget https://raw.githubusercontent.com/CityOfZion/standards/master/assets/nodes/supervisord.confchmod 700 supervisord.confcp supervisord.conf /etc/supervisord.confsudo supervisord添加初始脚本以便在系统重启时自动运行。这就是全部步骤,现在登出服务器并仅在必须部署更新或检测到恶意行为时再重新登录。其他安全推荐GRUB密码磁盘加密开启TCP SYN Cookie功能(net.ipv4.tcp_syncookies = 1 -> /etc/sysctl.conf)原文翻译自CoZ:https://github.com/CityOfZion… ...

December 24, 2018 · 3 min · jiezi

Stars数量非常高的Github Page

今天给大家分享几个优秀的Github Page。更多Github Page可以使用这个工具搜索 - Github Page搜索工具 https://man-ing.com/github。1. cs231n.github.iostars: 5521这个网站包含斯坦福大学CS课程的笔记和作业。2. bootflat.github.iostars: 4289BOOTFLAT是一个基于Bootstrap 3.3.0 CSS框架的开源Flat UI KIT。 它为Web开发人员提供了一种更快,更简单,重复性更低的方式来创建优雅的Web应用程序。3. huxpro.github.iostars: 3668黄玄的博客My Blog / Jekyll Themes / PWA4. tmallfe.github.iostars: 3649天猫前端5. dirtycow.github.iostars: 2275Dirty COW6. handong1587.github.iostars: 2072有趣的论文,项目,网站,博客和阅读/学习笔记。7. docker.github.iostars: 2041Docker文档的源代码库8. varharrie.github.iostars: 1752个人博客9. nndl.github.iostars: 1543《神经网络与深度学习》 Neural Network and Deep Learning10. microsoft.github.iostars: 1518Microsoft on GitHub更多信息Share CodeGithub Search Tools

December 23, 2018 · 1 min · jiezi

开源工具 | 手游自动化框架GAutomator,新增iOS系统和UE4引擎支

作者:WeTest小编商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处。原文链接:https://wetest.qq.com/lab/view/430.htmlWeTest 导读GAutomator是腾讯WeTest推出的手游自动化测试框架,已用于腾讯多个手游项目组的自动化测试。 _1、GAutomator诞生背后研究过手游自动化测试的同学都知道,虽然市场上已经有比较多成熟的自动化工具,如Android系统的UIAutomator,iOS的XCUITest和基于这些服务封装而来的 appium和wda等,但都无法直接应用到手游自动化中。问题的关键原因是手游与其他APP在自动化测试技术上有巨大的不同,普通的APP是由Android/IOS的标准UI控件组成,而手游画面上几乎所有内容都是通过游戏引擎渲染生成。为了填补这块技术空白,帮助更多的手游快速实现自动化,我们在2016年推出了GAutomator(下文简称GA)这个业界第一款手游自动化框架。GA是基于游戏引擎识别UI控件的手游自动化测试框架,并已经应用于众多明星手游项目,如《王者荣耀》、《乱世王者》、《火影忍者》等,他可以使手游中一些传统工具无法识别的UI控件变得可识别,进而可自动化测试。GAutomator重大更新: 将支持U3d&UE4双引擎、iOS&Android双系统继2016年我们首次开源了GA的框架代码,陆续有开发者给GA提出了非常好的建议被我们采纳并修复,但一直没有发布重大更新。直至2018年,通过厚积薄发,我们将向各位献上 3个重大更新:一、GA将支持UE4引擎2018年绝对求生火热起来后,WeTest团队也迎合趋势在第一时间支持了UE4引擎的手游自动化;二、GA将首次支持iOS系统和Android系统一样,iOS也同样支持录制回放的能力,方便大家快速实现自动化;三、GA SDK将伴随框架同步开源以开放的姿态邀请更多的开发者和我们一起共建;通过这次更新,GA实现了对U3d&UE4双引擎、iOS&Android双系统的支持,将满足大部分手游开发者的自动化测试需求。2、GAutomator后续计划为了让GA更加易用,使用起来效率更高,我们正在开发一个完整的IDE来降低环境部署时间及自动化脚本生成时间。新的IDE将同时识别支持标准安卓控件和unity/ue4引擎控件,此外,还将整合基于图像识别的测试方式让开发者可以快速创建自己的自动化测试脚本。我们还准备将IDE和WeTest平台的云服相结合,所有通过IDE生成的自动化测试脚本将支持一键提交到WeTest平台的测试云上,快速的完成分布式的功能测试任务。此外,GA 正在开发适合小游戏(Cocos Creator游戏引擎)的自动化测试框架,该项技术是由腾讯互娱光子工作室技术中心开发。小游戏测试框架将支持手机和Chrome浏览器上运行,该框架提供Chrome用例调试插件,可以快速在Chrome浏览器上直接编辑用例和调试用例。同时还提供了基于dijkstra算法扩展出来的随机测试。GAutomator的2年生命离不开各位开发者的关注和贡献,希望大家在访问GA的Github的专区时也留下你的看法和建议,WeTest团队将会非常欢迎和重视各位的想法,也期待和各位的进一步交流。关于腾讯WeTest腾讯WeTest是由腾讯官方推出的一站式品质开放平台。十余年品质管理经验,致力于质量标准建设、产品质量提升。腾讯WeTest为移动开发者提供兼容性测试、云真机、性能测试、安全防护、企鹅风讯(舆情分析)等优秀研发工具,为百余行业提供解决方案,覆盖产品在研发、运营各阶段的测试需求,历经千款产品磨砺。金牌专家团队,通过5大维度,41项指标,360度保障您的产品质量。—点击:https://github.com/Tencent/GAutomator 最新的GAutomator开源代码及资料。如果使用当中有任何疑问,欢迎联系腾讯WeTest企业QQ:2852350015

December 21, 2018 · 1 min · jiezi

Zilliqa 3.0版公测网正式上线 — — 代号“猫山王”

2018年11月30日Xinshu Dong发布于Zilliqa博客,Rita译今年6月底,我们推出了2.0版Zilliqa公测网,它具有文档、智能合约Scilla集成开发环境等重要功能,使开发人员能够在我们的平台上编写和测试他们的智能合约。今天,我们很高兴地向大家宣布:代号为“猫山王”的3.0版Zilliqa公测网现正式上线!同之前测试网的代号一样,“猫山王”是我们十分喜欢的榴莲品种。“猫山王”测试网正式上线标志着我们在技术上又达到了新的里程碑 — —据我们所知,这是世界上首个同时实现网络分片、交易分片以及智能合约分片的成熟测试网。这个测试网还成功解决了我们安全审计公司发现的一些潜在的安全问题,添加了必要的安全检查机制和额外的执行机制,提高了系统的稳定性。“猫山王”包含了许多与即将推出的主网相同的功能,其中包括在整个网络中全面实施安全智能合约语言Scilla、全面实现智能合约的分片https://blog.zilliqa.com/prov…。可以说,这个版本的测试网能够让我们对即将推出的主网核心功能进行更大规模的公开测试,标志着我们向2019年1月31日发布主网又迈出了关键一步(路线图可查阅)。和以前一样,测试网地址为https://explorer.zilliqa.com,测试钱包地址为https://wallet.zilliqa.com.我们将于12月3日星期一发布挖矿的全部细节和有关文件,为我们的矿工提供及时的技术和错误修复支持。对于社区的非榴莲爱好者,您也可以通过此链接(https://guide.michelin.com/sg…)来了解更多关于猫山王、红虾(1.0测试网代号)和D24(2.0测试网代号)之间的区别。为什么"猫山王"至关重要标志着Zilliqa的技术里程碑,据我们所知世界上首个同时实现网络分片、交易分片以及智能合约分片https://blog.zilliqa.com/prov…。对所有矿工开放Zilliqa挖矿https://blog.zilliqa.com/with…。正是因为在Zilliqa网络中PoW仅用于建立矿工身份而不用于达成共识,因此GPU只需要在一定时间段内满负荷运行PoW一小段时间,意味着Zilliqa挖矿效率更高、更经济环保。全面实施智能合约语言Scilla,新加入的节点也可以执行智能合约。(在过去的测试网中,Scilla仅在20至40个节点的小组上实现,仅用于开发人员测试。)实施新的矿工奖励机制,即通过考虑矿工对共识协议的贡献来计算奖励,例如通过计算矿工在过去几个周期中签署的次数。新的升级协议使节点可以轻松、无缝地升级到最新版本。“猫山王"挖矿如上所述,我们将于周一(2018年12月3日)发布“猫山王”的详细挖矿指南。在此公测网上挖矿的建议配置如下:Ubuntu 16.04.5 64位操作系统Intel i5处理器或更高版本8GB DRR3内存或更高可选:GPU显卡(例如3GB 显存的1 x GTX 1060)NAT环境下支持UPnP的路由器(较新款的路由器应当都具有此功能)https://en.wikipedia.org/wiki…“猫山王"主要新功能如果您一直关注我们的双周报,就会很熟悉“猫山王”的这些新功能。过去几个月,我们一直在努力实现这些功能,现在我们很高兴邀请矿工和开发人员公开测试这些功能。核心协议:支持智能合约分片GPU挖掘Coinbase奖励燃料费奖励燃料费计价PoW难度调整升级协议Gossip协议多输入、多输出(MIMO)DS委员会事件和日志交易收据合并的PoW:将两轮PoW合并为一轮节点恢复机制状态增量转发迁移到Kubernetes档案节点Scilla语言:类型检查器静态分析器和检查器套件用于添加新通道和分析的API燃料费核算改进了映射访问支持活动开发工具:改进的集成开发环境Savant:https://savant-ide.zilliqa.com/改进的JavaScript库:https://github.com/Zilliqa/Zi…RPC服务器Kaya:https://github.com/Zilliqa/kaya更多技术细节核心协议PoW挖矿的GPU选择:“猫山王”支持GPU挖矿。矿工可以将一个或多个GPU单元用于一个节点。例如,如果一个矿工拥有6个可用的GPU,那么他可以使用这6个GPU的任意组合来配置一个Zilliqa节点,方式可以是启动多个Zilliqa节点也可以是每个节点分配给一个特定的GPU等。Coinbase奖励:在这个测试网中,只要矿工参与了共识协议,无论是DS节点还是普通分片节点都可以获得测试币Zil。具体而言,节点在交易周期中对区块进行签名来参与共识协议的次数越多,就会获得越多的测试币。例如,一个节点如果既参与了微块又参与了终块的签名,那么它就可以贡献两个签名,因此可以获得两次奖励。燃料费奖励:公链的一个重要特征是通过燃料费向矿工分配奖励。在本版本的测试网中,处理交易产生的燃料费将被累积到总的coinbase奖励中,并在下一轮PoW提交开始前分发。燃料费计价:此前,我们的网络在处理交易时已把燃料费的消耗和限制计算在内,但实际的燃料费定价机制是在近期实现的。我们新编写的燃料费定价机制工作流程如下:首先,矿工们在提交工作量证明PoW时也同步提交愿接受的最低燃料费价格;之后,DS委员会就即将到来的DS周期内网络可接受的整体最低燃料费价格达成共识;最后,网络仅接受燃料费价格大于等于既定最低价的交易。智能合约的分片:几个月前,我们发布了一篇博文(https://blog.zilliqa.com/prov…,详细介绍了Zilliqa如何实现智能合约的分片。在Zilliqa中,DS委员会处理一些特定类型的智能合约交易。因此,DS委员会现在将进行额外的共识轮来验证这些交易。在DS委员会收到有关由分片验证的交易数据后,就会发生这种情况。升级协议:我们认为,为了确保系统的安全性,网络必须具有升级更新和补丁升级的功能。我们已经实现了升级协议的首个版本。举个例子,如果有一方(比如https://latest-release.zilliq…、SHA-256值二进制文件的最新版本,那么软件版本将被存储在名为VERSION的独立文件中,其中包含版本信息、预期DS周期和SHA-256值的信息。节点恢复:节点恢复是我们在过去几周内完成的重要功能之一。如果节点因任何原因,如完成升级等掉线而重新启动,它将读取存储在机器数据库中的持久数据(如DS委员会成员、分片结构、DS块和终块等数据)以恢复其最后的已知状态并开始与网络的其余部分重新同步。智能合约语言Scilla类型检查器和静态分析器套件:Scilla现在包括一套静态分析器和检查器。这些检查器的目标是帮助开发人员编写的智能合约能够通过一些基本到高级安全检查。例如,Scilla附带的一个类型检查器就可以用于检查合约的类型安全,而类型安全程序可以消除运行时可能出现的一些问题。另一个检查器(也是套件的一部分)可以检查编好的代码是否模式匹配。例如对于一个合约,它可以检查模式匹配的所有使用是否涵盖所有可能的分支,因此是穷举式的。它还检查无法访问的模式。当输入导致未处理的分支时,非穷举模式匹配可能在运行时导致错误。这些检查器将由每个矿工在部署时运行,因此可以为在Zilliqa上的合约提供基本的但强大的安全保障。更高级的静态分析器,如现金流分析器和燃料费分析器,也可供终端用户或IDE等客户使用。但分析器不是由矿工运行的。更多有关这些分析器的信息请参阅我们之前发布的双周报。实际上,您也可以使用我们的API将任何其他静态分析器添加到套件中。欢迎您加入我们的Github(https://github.com/Zilliqa/sc…。一些理想的检查器应当具有:1、未使用的变量;2、没有整数溢出;3、至少有一个转换接受付款。燃料费计算:在之前版本的测试网中,处理智能合约往往需要恒定的燃料费成本,而不管计算性质如何。此版本的测试网带有适当的燃料费计算机制,其中每个表达、声明、读取、写入、存储等都具有明确的成本。此链接https://drive.google.com/file…。注意:这些燃气费标价可能发生变化。支持事件:Scilla合约能够发出客户可以收听的事件。支持事件需要在语言中添加新构造,以及在区块链端添加对交易接收的支持。但是事件尚未编制索引,因此查询满足某些条件的事件(例如包含特定参数的所有事件)是不可行的。我们计划在下一版本中支持高效查询。开发工具为了促进智能合约的开发,我们还提供了一些之前已经公开过的开发项目,其中包括全新的Savant IDE:https://savant-ide.zilliqa.com/。我们推荐大家更多使用这个新的IDE,不再使用以前的JSON文件模拟区块链层的IDE,因为Savant对开发人员更加友好,终端用户不再需要手动编写JSON文件。IDE通过维护内存(在浏览器中)区块链来工作。如果你还没有尝试过,那就赶快来试一试吧!这个IDE还预装了一些可以直接玩的Scilla合约样本。我们还修改了JavaScript库,以改善客户端和网络之间的交互。此外,还提供了一个基于RPC的内部智能合约测试框架,名为Kaya:https://github.com/Zilliqa/kaya。我们也真诚地邀请开发人员来试用这些工具,并及时向我们反馈您的意见!如果您支持和信任Zilliqa,欢迎转发到朋友圈,让更多的人认识Zilliqa。如果您对项目有什么疑问,欢迎到评论区留言,我们会及时、认真回复每一个问题!END -

December 21, 2018 · 1 min · jiezi

Github Page 搜索工具更新 -- 索引数量扩容

Github Page索引数量目前Github Page可以索引到的数量是两万多条(26863),随着访问者使用这个工具的次数,可索引到的Github Page数量也随之增加。Github Page官网可以检索到所有的搜索结果,但是每次可以查看到的搜索结果限制在1000个(具体测试过程可以参考这篇文章),因此本工具提供了一次可以获取到更多结果的方式。更多信息Github Page搜索工具链接 https://man-ing.com/githubShare Code 我的个人网站

December 21, 2018 · 1 min · jiezi

十分钟成为 Contributor 系列 | 支持 AST 还原为 SQL

作者:赵一霖背景知识SQL 语句发送到 TiDB 后首先会经过 parser,从文本 parse 成为 AST(抽象语法树),AST 节点与 SQL 文本结构是一一对应的,我们通过遍历整个 AST 树就可以拼接出一个与 AST 语义相同的 SQL 文本。对 parser 不熟悉的小伙伴们可以看 TiDB 源码阅读系列文章(五)TiDB SQL Parser 的实现。为了控制 SQL 文本的输出格式,并且为方便未来新功能的加入(例如在 SQL 文本中用 “*” 替代密码),我们引入了 RestoreFlags 并封装了 RestoreCtx 结构(相关源码):// RestoreFlags 中的互斥组:// [RestoreStringSingleQuotes, RestoreStringDoubleQuotes]// [RestoreKeyWordUppercase, RestoreKeyWordLowercase]// [RestoreNameUppercase, RestoreNameLowercase]// [RestoreNameDoubleQuotes, RestoreNameBackQuotes]// 靠前的 flag 拥有更高的优先级。const ( RestoreStringSingleQuotes RestoreFlags = 1 << iota …)// RestoreCtx is Restore context to hold flags and writer.type RestoreCtx struct { Flags RestoreFlags In io.Writer}// WriteKeyWord 用于向 ctx 中写入关键字(例如:SELECT)。// 它的大小写受 RestoreKeyWordUppercase,RestoreKeyWordLowercase 控制func (ctx *RestoreCtx) WriteKeyWord(keyWord string) { …}// WriteString 用于向 ctx 中写入字符串。// 它是否被引号包裹及转义规则受 RestoreStringSingleQuotes,RestoreStringDoubleQuotes,RestoreStringEscapeBackslash 控制。func (ctx *RestoreCtx) WriteString(str string) { …}// WriteName 用于向 ctx 中写入名称(库名,表名,列名等)。// 它是否被引号包裹及转义规则受 RestoreNameUppercase,RestoreNameLowercase,RestoreNameDoubleQuotes,RestoreNameBackQuotes 控制。func (ctx *RestoreCtx) WriteName(name string) { …}// WriteName 用于向 ctx 中写入普通文本。// 它将被直接写入不受 flag 影响。func (ctx *RestoreCtx) WritePlain(plainText string) { …}// WriteName 用于向 ctx 中写入普通文本。// 它将被直接写入不受 flag 影响。func (ctx *RestoreCtx) WritePlainf(format string, a …interface{}) { …}我们在 ast.Node 接口中添加了一个 Restore(ctx *RestoreCtx) error 函数,这个函数将当前节点对应的 SQL 文本追加至参数 ctx 中,如果节点无效则返回 error。type Node interface { // Restore AST to SQL text and append them to ctx. // return error when the AST is invalid. Restore(ctx *RestoreCtx) error …}以 SQL 语句 SELECT column0 FROM table0 UNION SELECT column1 FROM table1 WHERE a = 1 为例,如下图所示,我们通过遍历整个 AST 树,递归调用每个节点的 Restore() 方法,即可拼接成一个完整的 SQL 文本。值得注意的是,SQL 文本与 AST 是一个多对一的关系,我们不可能从 AST 结构中还原出与原 SQL 完全一致的文本,因此我们只要保证还原出的 SQL 文本与原 SQL 语义相同 即可。所谓语义相同,指的是由 AST 还原出的 SQL 文本再被解析为 AST 后,两个 AST 是相等的。我们已经完成了接口设计和测试框架,具体的Restore() 函数留空。因此只需要选择一个留空的 Restore() 函数实现,并添加相应的测试数据,就可以提交一个 PR 了!实现 Restore() 函数的整体流程请先阅读 Proposal、Issue在 Issue 中找到未实现的函数在 Issue-pingcap/tidb#8532 中找到一个没有被其他贡献者认领的任务,例如 ast/expressions.go: BetweenExpr。在 pingcap/parser 中找到任务对应文件 ast/expressions.go。在文件中找到 BetweenExpr 结构的 Restore 函数:// Restore implements Node interface.func (n *BetweenExpr) Restore(ctx *RestoreCtx) error { return errors.New(“Not implemented”)}实现 Restore() 函数根据 Node 节点结构和 SQL 语法实现函数功能。参考 MySQL 5.7 SQL Statement Syntax写单元测试参考示例在相关文件下添加单元测试。运行 make test,确保所有的 test case 都能跑过。提交 PRPR 标题统一为:parser: implement Restore for XXX 请在 PR 中关联 Issue: pingcap/tidb#8532示例这里以实现 BetweenExpr 的 Restore 函数 PR 为例,进行详细说明:首先看 ast/expressions.go:我们要实现一个 ast.Node 结构的 Restore 函数,首先清楚该结构代表什么短语,例如 BetweenExpr 代表 expr [NOT] BETWEEN expr AND expr (参见:MySQL 语法 - 比较函数和运算符)。观察 BetweenExpr 结构:// BetweenExpr is for “between and” or “not between and” expression.type BetweenExpr struct { exprNode // 被检查的表达式 Expr ExprNode // AND 左侧的表达式 Left ExprNode // AND 右侧的表达式 Right ExprNode // 是否有 NOT 关键字 Not bool}3. 实现 BetweenExpr 的 Restore 函数:// Restore implements Node interface.func (n *BetweenExpr) Restore(ctx *RestoreCtx) error { // 调用 Expr 的 Restore,向 ctx 写入 Expr if err := n.Expr.Restore(ctx); err != nil { return errors.Annotate(err, "An error occurred while restore BetweenExpr.Expr") } // 判断是否有 NOT,并写入相应关键字 if n.Not { ctx.WriteKeyWord(" NOT BETWEEN ") } else { ctx.WriteKeyWord(" BETWEEN ") } // 调用 Left 的 Restore if err := n.Left.Restore(ctx); err != nil { return errors.Annotate(err, "An error occurred while restore BetweenExpr.Left") } // 写入 AND 关键字 ctx.WriteKeyWord(" AND ") // 调用 Right 的 Restore if err := n.Right.Restore(ctx); err != nil { return errors.Annotate(err, "An error occurred while restore BetweenExpr.Right ") } return nil}接下来给函数实现添加单元测试, ast/expressions_test.go:// 添加测试函数func (tc *testExpressionsSuite) TestBetweenExprRestore(c *C) { // 测试用例 testCases := []NodeRestoreTestCase{ {“b between 1 and 2”, “b BETWEEN 1 AND 2”}, {“b not between 1 and 2”, “b NOT BETWEEN 1 AND 2”}, {“b between a and b”, “b BETWEEN a AND b”}, {“b between ’’ and ‘b’”, “b BETWEEN ’’ AND ‘b’”}, {“b between ‘2018-11-01’ and ‘2018-11-02’”, “b BETWEEN ‘2018-11-01’ AND ‘2018-11-02’”}, } // 为了不依赖父节点实现,通过 extractNodeFunc 抽取待测节点 extractNodeFunc := func(node Node) Node { return node.(*SelectStmt).Fields.Fields[0].Expr } // Run Test RunNodeRestoreTest(c, testCases, “select %s”, extractNodeFunc)}至此 BetweenExpr 的 Restore 函数实现完成,可以提交 PR 了。为了更好的理解测试逻辑,下面我们看 RunNodeRestoreTest:// 下面是测试逻辑,已经实现好了,不需要 contributor 实现func RunNodeRestoreTest(c *C, nodeTestCases []NodeRestoreTestCase, template string, extractNodeFunc func(node Node) Node) { parser := parser.New() for _, testCase := range nodeTestCases { // 通过 template 将测试用例拼接为完整的 SQL sourceSQL := fmt.Sprintf(template, testCase.sourceSQL) expectSQL := fmt.Sprintf(template, testCase.expectSQL) stmt, err := parser.ParseOneStmt(sourceSQL, “”, “”) comment := Commentf(“source %#v”, testCase) c.Assert(err, IsNil, comment) var sb strings.Builder // 抽取指定节点并调用其 Restore 函数 err = extractNodeFunc(stmt).Restore(NewRestoreCtx(DefaultRestoreFlags, &sb)) c.Assert(err, IsNil, comment) // 通过 template 将 restore 结果拼接为完整的 SQL restoreSql := fmt.Sprintf(template, sb.String()) comment = Commentf(“source %#v; restore %v”, testCase, restoreSql) // 测试 restore 结果与预期一致 c.Assert(restoreSql, Equals, expectSQL, comment) stmt2, err := parser.ParseOneStmt(restoreSql, “”, “”) c.Assert(err, IsNil, comment) CleanNodeText(stmt) CleanNodeText(stmt2) // 测试解析的 stmt 与原 stmt 一致 c.Assert(stmt2, DeepEquals, stmt, comment) }}**不过对于 ast.StmtNode(例如:ast.SelectStmt)测试方法有些不一样,由于这类节点可以还原为一个完整的 SQL,因此直接在 parser_test.go 中测试。**下面以实现 UseStmt 的 Restore 函数 PR 为例,对测试进行说明:Restore 函数实现过程略。给函数实现添加单元测试,参见 parser_test.go:在这个示例中,只添加了几行测试数据就完成了测试:// 添加 testCase 结构的测试数据{“use select”, true, “USE select”},{“use sel``ect”, true, “USE sel``ect”},{“use select”, false, “USE select”},我们看 testCase 结构声明:type testCase struct { // 原 SQL src string // 是否能被正确 parse ok bool // 预期的 restore SQL restore string}测试代码会判断原 SQL parse 出 AST 后再还原的 SQL 是否与预期的 restore SQL 相等,具体的测试逻辑在 parser_test.go 中 RunTest()、RunRestoreTest() 函数,逻辑与前例类似,此处不再赘述。加入 TiDB Contributor Club,无门槛参与开源项目,改变世界从这里开始吧(萌萌哒)。 ...

December 21, 2018 · 4 min · jiezi

Github Page 搜索工具

轮子今天造了一个轮子 – Github Page搜索工具 https://man-ing.com/github。什么是Github Page直接从GitHub存储库托管。只需编辑,推送,更改即可生效。关于Github Page的更多信息可以点击这个链接了解一下【Github Page】如何创建一个属于自己的Github PageGithub+Hexo+Next创建自己的博客关于这个轮子搜索你可以在搜索框中输入你想要查找的Github Page关键词,点击搜索Github Page按钮即可获取搜索结果。Top Github PageGithub Page搜索主页,会根据Github Page Stars的数量来进行排名,显示Top10的Github Page。Github Page信息摘要在Github Page详情页,你可以查看当前Github Page仓库的Stars数量,Score分数,以及编程语言。你还可以选择点击去Github Page站点,还是去Github Page仓库。当详情页下方是Github Page的readme信息,你也可以通过阅读这些信息来了解这个仓库的大概内容。为啥要造这个轮子就是发现其实有很多优秀的Github Page,但是比较分散,当然你也可以直接去Github官网进行搜索,这个工具提供了一种更方便的访问Github Page的方法。以后会提供更多扩展功能。更多信息Github Page搜索工具链接 https://man-ing.com/githubShare Code 我的个人网站

December 20, 2018 · 1 min · jiezi

NEO 客户端插件

从 NEO 2.9.0 开始,一些附加功能被独立封装在插件中用以调用,目的是为了提升节点的安全性,稳定性和灵活性。用户可以自行选取所需要的扩展功能而不用每次在启动 NEO-CLI时通过附加参数来调用,避免了很多人为的失误操作同时简化了打开钱包,调用 API 等一系列繁琐的指令。点击此处下载 Plugins。备注:ApplicationLogsImportBlocksRpcSecuritySimplePolicyStatesDumper安装插件要安装插件,在客户端根目录下新建 Plugins 文件夹(注意首字母大写),然后将解压出来的插件拷贝到其中,如下所示在neo-cli根目录下安装:插件中的 API 接口getapplicationlog 方法根据指定的 NEP-5 交易 ID 获取合约日志。完整的合约日志会记录到 ApplicationLogs 目录。此方法由插件提供,需要安装 ApplicationLogs 插件才可以调用。参数说明txid:交易ID调用示例请求正文:{ “jsonrpc”: “2.0”, “method”: “getapplicationlog”, “params”: [“0xff488264c1abf9f5c3c17ed8071f6dd3cd809b25797a43af49316490ded8fb07”], “id”: 1}{ “jsonrpc”: “2.0”, “id”: 1, “result”: { “txid”: “0xff488264c1abf9f5c3c17ed8071f6dd3cd809b25797a43af49316490ded8fb07”, “executions”: [ { “trigger”: “Application”, “contract”: “0x0110a8f666bcc650dc0b544e71c31491b061c79e”, “vmstate”: “HALT, BREAK”, “gas_consumed”: “2.855”, “stack”: [ { “type”: “Integer”, “value”: “1” } ], “notifications”: [ { “contract”: “0xb9d7ea3062e6aeeb3e8ad9548220c4ba1361d263”, “state”: { “type”: “Array”, “value”: [ { “type”: “ByteArray”, “value”: “7472616e73666572” }, { “type”: “ByteArray”, “value”: “e3069da508f128069a0cd2544b0728ccbacdfb43” }, { “type”: “ByteArray”, “value”: “d142f89e93b2717426a8130c37dad93aad70cff5” }, { “type”: “ByteArray”, “value”: “00e1f50500000000” } ] } } ] } ] }}说明: 其中 gas_consumed 表示该交易消耗的 gas 数量,即交易手续费。每笔交易会有10 gas 的免费额度。如果数量小于10,则不收取手续费,如果大于10,那么收取超过10的那部分作为手续费并向上取整。例如 gas_consumed = 12.3,那么实际收取的手续费为3 gas. ...

December 20, 2018 · 1 min · jiezi

NEO 节点介绍

全节点(full nodes)是存储 NEO 区块链全部数据的节点,通过 P2P 的方式与区块链网络连接,在区块链网络中,所有的全节点都是平等的,既充当客户端又充当服务器。NEO 有两个全节点程序:NEO-GUI: 面向普通用户,提供图形界面,具有除共识外的所有功能。NEO-CLI: 面向开发者,提供命令行界面,具有一些钱包操作的基本功能,除此之外还会对外提供 API,可以与其它节点达成共识,参与区块的生成。NEO 节点下载地址Neo-GUI 程序下载地址 https://github.com/neo-projec...Neo-GUI 源代码 https://github.com/neo-projec...Neo-CLI 程序下载地址 https://github.com/neo-projec...Neo-CLI 源代码 https://github.com/neo-projec...GUI 节点与 CLI 节点功能比较端口说明/NEO-CLI安全策略如果你想让外部程序访问该节点的 API 需要开放防火墙端口,以下是端口说明。NOTE强制要求:必须使用白名单或防火墙以屏蔽外部服务器请求,否则会有重大安全隐患。NEO-CLI 本身不提供远程开关钱包功能,打开钱包时也没有验证过程。因此,安全策略由用户根据自身情况制定。对于交易所,由于钱包要一直保持打开状态以便处理用户的提现,因此,从安全角度考虑,钱包必须运行在独立的服务器上,并参考下表配置好端口防火墙。原文:http://docs.neo.org/zh-cn/nod…

December 19, 2018 · 1 min · jiezi

NEO共识算法图解

共识机制术语说明权益证明 PoS :一种利用网络协商一致来处理容错的算法。工作量证明 PoW :一种利用计算能力来处理容错的算法。拜占庭错误 BF: 一个节点保持功能,但以不诚实甚至是恶意的方式来工作。dBFT(一种改进的拜占庭容错算法) dBFT :NEO 区块链中的共识算法,该算法通过多个共识节点的协商来达成共识,有良好的可用性和最终性。视图 V :在 dBFT 算法中,一次共识从开始到结束所使用的数据集合,称为视图。规则在 NEO 的共识算法中,共识节点由 NEO 持有人投票选出,并对区块链中交易的有效性进行验证。过去这些节点被称作“记账人”,现在他们被称作”共识节点”。共识节点:此节点参与共识行为。 在共识行为中, 共识节点轮流担任以下两个角色:议长 (一人) :议长 负责向系统发送一个新的区块的提案。议员 (多人) :议员 负责对议长的提案进行投票,大于等于2/3的议员投票时,提案通过。介绍众多区块链共识算法的根本区别是他们如何保障对系统中的故障节点、恶意节点的容错能力。传统的 PoW 方法可以提供这种容错能力,只要网络的大多数算力是诚实的。然而,由于这种模式依赖于大量的计算,这种机制可能会非常低效且不环保(算力消耗能源,需要硬件)。 这些依赖就是 PoW 方法的限制所在,最主要的就是扩展的成本。NEO实现了一种委托的拜占庭容错共识算法,它借鉴了一些 PoS 的特点(NEO持有人需要对共识节点进行投票) 利用最小的资源来保障网络免受拜占庭故障的影响,同时也弥补了 PoS 的一些问题。该解决方案解决了与当前块链实现相关的性能和可扩展性问题,而不会对容错产生重大影响。理论拜占庭位于如今的土耳其的伊斯坦布尔,是东罗马帝国的首都。由于当时拜占庭罗马帝国国土辽阔,为了防御目的,因此每个军队都分隔很远,将军与将军之间只能靠信差传消息。 在战争的时候,拜占庭军队内所有将军和副官必需达成一致的共识,决定是否有赢的机会才去攻打敌人的阵营。但是,在军队内有可能存有叛徒和敌军的间谍,左右将军们的决定又扰乱整体军队的秩序。在进行共识时,结果并不代表大多数人的意见。这时候,在已知有成员谋反的情况下,其余忠诚的将军在不受叛徒的影响下如何达成一致的协议,拜占庭问题就此形成。为了描述 DBFT 的工作原理,我们将本节重点放在第 5 部分中的证明 66.66% 的共识率的正确性。请记住,不诚实的节点不需要主动恶意,因为它根本不可能是按预期运作。为了讨论,我们将描述一些情景。 在这些简单的例子中,我们假设每个节点沿着从 议长 发送过来的消息发送。 此技工也用于DBFT,对系统至关重要。 我们将仅描述功能系统与功能失效系统之间的区别。 有关更详细的说明,请参阅参考资料。诚实的议长图 1: 一个 n = 3 的例子中存在一个不诚实的 议员。在图 1 中,我们有一个诚实的 议员 (50%)。两个 议员 从 议长 那里收到相同的消息,然而,由于其中一个 议员 不是诚实的,诚实的议员只能确定有不诚实的节点,但无法识别它是 议长 还是 议员。因此 议员 必须弃票,改变视图。图 2: 一个 n =4 的例子中存在一个不诚实的 议员。在图 2 中,我们有两个诚实的 议员 (66%)。所有的 议员 从 议长 那里收到相同的消息,然后向其它 议员 发送消息和自己的验证结果。根据两位诚实 议员 的共识,我们可以确定 议长 或者右边的 议员 在系统中是不诚实的。不诚实的议长图 3: 一个 n = 3 的例子中存在一个不诚实的 议长。在图 3 中,不诚实的是 议长,这和图 1 中描述的案例有同样的结论。议员 无法确定哪个节点是不诚实的。图 4: 一个 n = 4 的例子中存在一个不诚实的 议长。在图 4 所示的例子中,中间的节点和右边的节点接收的区块不可验证, 由于他们占到多数(66%),导致更换视图选举新议长。在这个例子中,如果诚实的议长向三名议员中的两名发送了诚实的数据,那么它将被验证而不需要改变视图。实际实施DBFT 在 NEO 中的实际实现使用迭代共识方法来保证达成共识。算法的性能取决于系统中诚实节点的分数。图5描绘了作为不诚实节点的函数的期望迭代。请注意,图5没有低于66.66%的共识节点诚信。在这个临界点和33.33%的共识节点诚信之间,有一个无人地带,那里无法达成共识。低于33.33%的共识节点诚信,不诚实的节点(假设它们达成共识)能够自己达成共识,并成为系统中新的真理点。图5: DBFT算法的 Monto-Carlo 模拟,描绘了达成共识所需的迭代。 {100 个节点;100,000 个模拟区块随机选择诚实节点}定义在算法中有如下定义:t:分配给区块生成的时间总量,以秒为单位。当前时间: t = 15 秒 这个值可以用来粗略估计单个视图迭代的持续时间,因为共识活动和通信事件相对于这个时间常数是快速的。n: 有效的共识节点数量。f:系统中故障共识节点的最小阈值。f = (n - 1) / 3h : 在共识活动期间的当前块高度。i : 共识节点索引。v : 共识节点视图。该视图包含节点在一轮共识中收到的汇总信息。 这包括所有议员发起的投票(prepareResponse 或 ChangeView)。k : 视图 v 的索引。共识活动可能需要多轮。在共识失败时,k值增加,新一轮的共识开始。p : 选为议长的共识节点索引。这个索引的计算机制在共识节点间轮流,以防止单个节点成为系统内的指令器。p = (h - k) mod (n)s: 安全共识的阈值。 低于这个阈值,表示网络故障。s = ((n - 1) - f)要求在NEO中,共识容错有三项主要要求:s 议员必须就一项交易达成共识,然后区块才可以实施。不诚实的共识节点不能说服故障交易的诚实共识节点。至少s Congressmen 处于同一状态 (h,k) ,开始达成共识。算法该算法工作原理如下:1.共识节点使用发送者的签名向全网广播一个交易。图 6:一个 共识节点 收到交易并在系统中广播2.共识节点将交易数据记录到本地存储器中。3.共识活动的第一个视图 v 被初始化。4.议长确定。图 7: 议长 确定且设置好视图等待 t 秒。 5.议长广播提案 <prepareRequest, h, k, p, bloc, [block]sigp>图 8:议长 提出区块提案,由众议员审阅。6.议员收到提案并验证:数据格式与系统规则是否一致?交易是否已经在链上?合同脚本是否正确执行?交易是否只包含一笔开支(即交易是否避免了双重开支的情况?)如果是有效的提案广播: <prepareResponse, h, k, i, [block]sigi>如果是无效的提案广播: <ChangeView, h,k,i,k+1> ‘图 9:议员 审阅区块提案并响应7.在收到 s 数量的 ‘prepareResponse’ 广播后,众议员达成共识并发布一个区块。8.议员签名该区块图 10: 达成共识,获批议员签名区块,并将其绑定到链上。9.当一个共识节点收到一个完整区块时,当前的视图数据被清除,并开始新一轮的共识。k = 0NOTE如果 ( ) 秒后在同一视图没有达成共识:共识节点进行广播:<ChangeView, h,k,i,k+1>一旦共识节点接收到至少 s 个表示相同视图改变的广播,就会增加视图 v, 并引发新一轮的共识。引用A Byzantine Fault Tolerance Algorithm for BlockchainPractical Byzantine Fault ToleranceThe Byzantine Generals Problem ...

December 19, 2018 · 2 min · jiezi

11月NEO技术社区开发进展汇总

为了帮助大家了解NEO平台上技术社区的开发进展,NEONewsToday将每月发布一份值得关注的更新报告。这些报告将包括对NEO核心项目的贡献以及对社区创建项目的改进。这个报告不是包括所有项目进展的详细清单。NEONewsToday将从尽可能多的社区贡献者中收集信息,但并不能完全包含所有社区项目内容。任何对NEO基础设施或开发工具做出重大贡献的NEO开发者(无论是开发社区的成员还是其他人),都可以通过wakeup@neonewstoday.com与NEONewsToday 联系,并提供相关信息以供将来报告使用。NEO协议贡献Neo-cli(NR)自10月24日以来,NeoResearch成员Igor和Vitor Coelho一直致力于一项旨在优化NEO共识机制的重大更新。该提案中还看到了CoZ和NGD成员的贡献和评论。https://github.com/neo-projec…此项更新的第一部分由PR #426涵盖,重点介绍如何添加“提交”阶段的共识,以防止“分叉”问题(移植到与Akka模型兼容的地方),以及更新策略和其他性能优化。再生策略的目的是允许丢失/失败的共识节点自动重新与网络连接,无需重新启动。https://github.com/neo-projec…初步的修改已经完成,目前正在接受NeoResearch、NGD和CoZ的测试。在不久的将来,新的具有这些改进的Neo-cli版本将被应用到测试网络共识节点中。社区项目Neon Wallet (CoZ)11月发布了Neon Wallet v2,对原有的Neon Wallet进行了全面的设计改进。本月进行了大量更改,包括实现了新功能,如NEP-9 QR生成、节点选择以及简单可转换的转移和Token销售优先费用。https://neonewstoday.com/gene…第一周是修复新特性和其他各种日常bug的修复https://github.com/CityOfZion…,例如将接收转移的最大数量从10个增加到25个https://github.com/CityOfZion…。Neon Wallet是去中心化的,已经不存在对其他项目的依赖https://github.com/CityOfZion…。其中一个例子就是Neoscan的自动节点选择,它被直接内置在Neon钱包中的自动节点选择算法所取代。https://github.com/CityOfZion…第一周,钱包的下载量就超过了15k。目前的短期目标包括处理小Bug、对测试的改进以及考虑替代定价数据API。该团队还开始计划对本地网络或手动节点地址输入、多团体钱包和改进的用户体验/用户界面的GAS索赔的支持。在最后一周,有20多个问题得到了解决,测试覆盖率和节点选择算法也得到了进一步的改进https://github.com/CityOfZion…。这将继续作为之后的核心关注点,因为该团队为第一次重大更新(即v2.0.1)打下了坚实的基础。neo-local (CoZ)11月的大部分开发都花在了改进和更新命令行界面(CLI)上https://github.com/CityOfZion…。 最初的改进之一是添加了destroy命令,允许根据需要停止和删除服务容器。 新的本地版本0.11.1于11月6日星期二发布。https://github.com/CityOfZion…从12日星期一开始的第二周,PrivateNet容器https://github.com/CityOfZion…、新python https://github.com/CityOfZion...://github.com/CityOfZion/neo-local/pull/108被添加到CLI。通过改进CLI,可以简化对CLI的理解,并建立用于开发的NEO私有链。从19日星期一开始的下一周,CLI中添加了neo-scan-sync https://github.com/CityOfZion...://github.com/CityOfZion/neo-local/pull/119,同时还添加了其他改进,如引导链的能力https://github.com/CityOfZion…。CLI启动了特性工作,目的是使其具有上下文智能;这意味着它知道哪些服务相互依赖,以便以正确的顺序启动它们https://github.com/CityOfZion…。11月的最后一周,CLI功能得到了改进,这些功能在项目的早期阶段得到了快速开发https://github.com/CityOfZion…。这些更改包括增加配置的灵活性,以及支持在任何操作系统上加载配置。改进还提供了进度条和其他信息,以帮助用户知道下载需要多长时间。https://github.com/CityOfZion…近期欧洲黑客马拉松(鹿特丹,柏林和苏黎世)也使用了NEO本地项目https://github.com/CityOfZion…。该团队报告称,已经收到了很多关于如何在未来改进项目的有用反馈。neo-python (CoZ)虽然neo-python节点在正常网络活动期间通常没有问题,但增加的使用/滥用行为可能导致neo-python节点卡住,需要重新启动。 neo-python团队已经投入时间分析此行为并更新网络代码以提高这些节点的整体弹性。bootstrapping进程也得到了改进,在这之前需要用户更新protocol.xxx.json文件中的bootstrap链接。该文件现在将自动查找最新版本https://github.com/CityOfZion…,因此用户无需进行手动修改。主网和测试网引导程序文件已更新,分别阻止高度3002xxx和2022xxx。更新后的实现能够通过在neo-python设置中指定路径来交换组件类https://github.com/CityOfZion…。虽然目前只支持RPC和REST服务器,但团队打算将其扩展为包括CLI命令,网络和数据库。最终,该团队希望允许用户创建可以通过pip包管理器安装的自我维护插件,然后通过更改protocol.xxx.json来激活。在向JSON-RPC服务器添加GET和OPTIONS请求功能之后,neo-python项目正在接入与neo-cli的功能奇偶校验https://github.com/CityOfZion…。在更新之后,使用neo-python的RPC节点将通过GET请求进行查询。neo-tools (CoZ)Neo-tools是一个更近期的项目,由City of Zion 的开发者Fetter带头发起https://github.com/CityOfZion…。 现采用CLI的形式,旨在将所有NEO API、示例和项目原语集中到一个位置,以便开发人员可以在简单的类Unix环境中轻松访问它们。Fetter从包含基本参考API实现和服务的初始构建开始。其中包括Neoscan,neon-js,Binance,Coinmarketcap和CoinPaprika的CLI。 根据这些内容,Fetter优先考虑被动地添加开发者社区需求最多或者其他成员所需的工具。在短期内,neo-tools的更新将侧重于一致性和代码模块化,但可以期待Fetter意图包纳所有NEO服务,包括RPC和REST API,钱包服务和区块链元数据分析/转换实用程序。NeoCompiler Eco (NR)NeoCompiler Eco是一个智能合约开发平台https://neocompiler.io/#/,由NeoResearch团队创建https://neonewstoday.com/gene…,该平台允许他们使用基于Linux的系统编译NEO合同。 NeoCompiler Eco可通过Web浏览器或移动设备访问,可帮助开发人员构建,测试甚至部署使用C#,Java,Python和Go编写的智能合约。编译器在共享的私有测试网络上运行,每12小时重置一次,允许任何人在部署到主网之前测试他们的合约。NeoResearch目前专注于实施共识绘制工具https://github.com/NeoResearc…,该工具旨在跟踪共识数据并以人类易于理解的格式呈现。该工具的实施还可以帮助调试NEO共识的所有潜在问题。Smart Account Composer (NR)Smart Account Composer/Smacco是另一个NeoResearch项目https://neoresearch.io/smacco/#/,允许创建具有附加功能的NEO地址。Smacco不是生成具有基本发送/接收功能的典型公钥/私钥对,而是允许用户使用特定规则创建验证合同。这些基于逻辑的条件可以以多种方式使用,例如用于创建需要多个私钥解锁的钱包,或者锁定时间戳后面的特定动作。它还可用于允许特定私钥使用某些操作,例如发送特定令牌,而无需完全控制帐户。与NeoCompiler Eco一样,用户可操作性是Smacco的重中之重。为了便于理解这些规则描述的逻辑流程,使用该工具创建的任何智能帐户都伴随着自动生成的逻辑图。无论技术熟练程度如何,这些图表都易于被任何人理解。原文:https://neonewstoday.com/deve…

December 19, 2018 · 1 min · jiezi

编辑commit信息的最佳实践

翻译自:Write good git commit message写好Git提交信息很长时间以来,我甚至都不知道编写 git 提交(commit)信息也有它自己的“最佳实践”。在我第一次接触 git 时,提交信息的那部分内容被描述为类似这样的话:"…and here you can write something short about what’s going on in the commit"。不好的提交信息请看下面的提交信息。如果你想合并它们,你真的不会知道哪些是添加的内容,哪些是修改的内容,它们做了什么或者你为什么需要它们。如果你想在历史记录中搜索某些内容,那么上述的糟糕情况同样会遇到。你向下滚动日志,但它仍是一团糟,并且浪费时间。cd3e27a contact pageaee9d0d commentseac95e5 list of online users, some other changes because of serverfae5636 little edit好的提交信息现在再看下这些提交信息。是不是感觉好多了?反正我是这么觉得。43ec6aa Fix error when the URL is not reachable4fe84ab Add error message if something went wrong753aa05 Add server fingerprint checkdf3a662 Fix shadow box closing problem如何编写好的提交信息整个 commit 信息应该有它的格式 - 主题、正文以及可选的由已解决/已关闭的问题组成的结论。主题Git 的 commit 帮助页面对提交信息的主题有个很不错的描述:对变更内容进行总结的单行文本(少于50个字符),后跟一个空行。主题应以大写字母开头且不以句点 . 结尾。而且重要的是,这必须是一个强制的形式。Chris Beams 为此写了一个简单的规则:Git 提交信息主题的形式应该总是能够符合这样的句式:如果提交被应用,那么这个提交将“你写的主题”。比如:【译注】这里可以把“你写的主题”理解成一个动词、一个动作。如果被应用,那么这个提交将删除(Delete)不需要的行。如果被应用,那么这个提交将添加(Add) grep 选项。如果被应用,那么这个提交将修复(Fix)协议缺失时错误。对于不好的提交信息,就不会符合这个句式:如果被应用,那么这个提交将Contact page。如果被应用,那么这个提交将list of online users, some other changes because of server。Git 本身就是使用这种方法。当你要合并某些内容时,Git 会生成一个类似这样的提交信息:“Merge branch…",或者回滚时生成 “Revert…"。正文在正文里你可以编写哪些内容被修改了以及为什么修改。正文的每一行不应超过72个字符。当然并不是每次提交都需要有正文。尾行最后,你可以添加此次 commit 修复的或相关的 issue。它可以是一个链接、数字或者如果你在使用 GitHub,你可以这样写:Resolves #N / Closes #N,这里的 N 表示 issue ID。示例这是一个来自我的仓库的提交信息示例:Fix error when protocol is missingFirst, it checks if the protocol is set. If not, it changes the url andadd the basic http protocol on the beginning.Second, it does a “preflight” request and follows all redirects andreturns the last URL. The process then continues with this URL.Resolves #17写在最后感谢你的阅读,希望你从中学到了一些新的东西。如果你有其他的关于如何编写更好的提交消息的提示,或者如何更好地使用此工具,请发表你的评论。生成变更日志这样编写提交信息的另一个好处就是很容易生成变更日志。# show whole commit history$ git log –oneline –decorate –color# show history from one tag to another$ git log 0.0.9..0.0.10 –oneline –decorate –color# show history form tag to headgit log 0.0.9..HEAD –oneline –decorate –color这种命令的结果就是你的提交列表。21629ee Fix getResources bugaa13384 Update docs44de44c Add HSTS checkCommitizen在 GitHub 上有个名叫 Commitizen 的命令行工具,它可以让提交信息的编写变得更容易。当你想提交的时候,你只需键入 git cz,它会问你一组问题,然后为你创建正确的提交信息。参考Git commit manpageClosing issues using keywords on GitHubHow to Write a Git Commit Message post from Chris BeamsAngular Commit Message Format ...

December 17, 2018 · 2 min · jiezi

dva开发一个cnode网站(1)

dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架。本教程是利用cnode的开放api来做一个一样的网站,以此来学习dva框架的使用。写的不好的地方还请多多包涵,大家一起学习。1.搭建工程安装 dva-cli$ npm install dva-cli -g$ dva -vdva-cli version 0.9.1创建新应用$ dva new cnode然后我们 cd 进入 cnode 目录,并启动开发服务器:$ cd cnode$ npm start几秒钟后,你会看到以下输出:Compiled successfully!The app is running at: http://localhost:8000/Note that the development build is not optimized.To create a production build, use npm run build.这样我们的cnode的应用就创建好了2.了解目录mock 产生假数据node_modules 项目依赖的第三方库public 放单页面模板文件,相当于访问的首页面src 开发目录src/assets 静态资源文件src/components 公共组件src/models 每个业务中处理的数据(state)src/routes 路由对应的页面src/services 业务逻辑处理utils 工具类3.修改首页现在我们要换掉欢迎页,去做我们自己的布局打开routes/IndexPage.js 修改代码import React from ‘react’;import { connect } from ‘dva’;function IndexPage() { return ( <div > <h1>Hello, this is cnode web site</h1> </div> );}IndexPage.propTypes = {};export default connect()(IndexPage);删除routes/IndexPage.css运行命令npm start4.加入antd ui库通过 npm 安装 antd 详见 repo和 babel-plugin-import 。babel-plugin-import 是用来按需加载 antd 的脚本和样式的,详见 repo$ npm install antd babel-plugin-import –save编辑 .webpackrc,使 babel-plugin-import 插件生效。{ “extraBabelPlugins”: [ [“import”, { “libraryName”: “antd”, “libraryDirectory”: “es”, “style”: “css” }] ]}5.使用antd创建头部公共组件在components下创建Header.jsimport React from ‘react’;import { Menu, Icon, Input } from ‘antd’;import PropTypes from ‘prop-types’;const Search = Input.Search;const Header = ({dispatch,keys}) => { function handleClick(e) { console.log(e.key) } return ( <div> <Menu onClick={handleClick} selectedKeys={keys} mode=“horizontal” > <Menu.Item key=“node” disabled> <Icon type=“tag” />CNODE </Menu.Item> <Menu.Item key=“search”> <Search placeholder=“input search text” onSearch={value => console.log(value)} enterButton /> </Menu.Item> <Menu.Item key=“index”> <a href="/"><Icon type=“appstore” />首页</a> </Menu.Item> <Menu.Item key=“into”> <a href="#/into"><Icon type=“appstore” />新手入门</a> </Menu.Item> <Menu.Item key=“api”> <a href="#/api"><Icon type=“appstore” />API</a> </Menu.Item> <Menu.Item key=“about”> <a href="#/about"><Icon type=“appstore” />关于</a> </Menu.Item> <Menu.Item key=“reg”> <a href="#/reg"><Icon type=“appstore” />注册</a> </Menu.Item> <Menu.Item key=“login”> <a href="#/login"><Icon type=“appstore” />登录</a> </Menu.Item> </Menu> </div> );};Header.propTypes = { keys: PropTypes.array.isRequired};export default Header;在routes/IndexPage.js中使用公共组件import React from ‘react’;import { connect } from ‘dva’;import Header from ‘../components/Header’;function IndexPage() { return ( <div > <Header keys={[‘index’]}/> <h1>Hello, this is cnode web site</h1> </div> );}IndexPage.propTypes = {};export default connect()(IndexPage);效果:下节课接着搞。欢迎关注我的公众号 mike啥都想搞,回复react免费领取视频教程。 ...

December 17, 2018 · 2 min · jiezi

NEO改进协议提案9(NEP-9)

文章目录1.摘要2.动机3.详述3.1原生资产转移URI3.1.1 URI键3.1.2 可用URI 键 映射 NEO 交易属性键3.1.3 参考原生资产地址3.1.4 例子3.2智能合约调用URI3.2.1 NEP-5 token 转移3.2.2 URI Keys3.2.33.2.3 例子4.原理5.实现6.参考摘要本NEP描述了一个用于NEO原生资产转移的URI标准。此外,它提议一个URI子集用于完善智能合约操作,在本提案所包含的NEP-5token被转移时。未来,随着更便于理解的和经过良好测试的合约被加入网络时,URI会被增加。我们定义了一个框架用于判定一个URI方案是否适用于下面的智能合约操作。动机目前,没有标准URI可供NEO客户端使用。比特币实现了一个URI标准,因此点击连接或扫描二维码可以轻松实现一个比特币支付。同样,NEO上的原生资产转移可应该如此简单易行然而,NEO可能可以通过智能合约的调用实现更多的合约操作。这包括token的铸造和转移,注册域名,交换和另外一些自定义应用。理想情况下,一个URI存在通用智能合约的调用。然而,这存在安全隐患,可能很难判定智能合约实际做了什么,可能因此导致资金的损失为了解决该问题,我们提议为通用智能合约调用生成的URI被限制在一个完善的合约操作的子集内(例如NEP-5 token的转移)。这将是得客户端轻松理解和验证给定的URI将要进行的操作。如果客户端更注重安全性,可以维护一个遵顼给定URI规范的合约白名单任何新的被添加进本提案的NEO URI对应的智能合约应当是安全、记录良好和广泛使用。我们将在本提案的最后进行更多的讨论详述原生资产转移URI原生资产的转移有以下URI。它描述了接收者地址,资产和伴随着交易发送的额外属性。URI 方案: neoneo:<address>[?asset=<asset>][?amount=<amount>][?<TransactionAttributeKey>=<value>]URI键可用URI 键 映射 NEO 交易属性键参考原生资产地址名字 哈希NEO c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9bGAS 602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7例子开始交易到指定地址neo:AeNkbJdiMx49kBStQdDih7BzfDwyTNVRfb开始未指定数量NEO的交易neo:AeNkbJdiMx49kBStQdDih7BzfDwyTNVRfb?asset=neo开始1NEO到指定地址的交易neo:AeNkbJdiMx49kBStQdDih7BzfDwyTNVRfb?asset=neo&amount=1.0开始1NEO到指定地址且交易描述为“hello”的交易neo:AeNkbJdiMx49kBStQdDih7BzfDwyTNVRfb?asset=neo&amount=1.0&description=Hello开始0.1GAS到指定地址的交易.将公钥放在ecdh02属性字段中以允许发件人使用ECDH加密.交易属性描述为“hello”neo:AQc5mtFayAdoCK13BW1cGAzAHyo9SoUWe7?asset= gas&amount=0.1&ecdh02=02ed53ad58c838435d4dd7a4b25c1eba01384c814ca53a539405434807afbb04b4&description=Hello这应该足够促使NEO原生资产的安全转移智能合约调用URI为了调用智能合约,我们需要指定脚本哈希和智能合约被调用的操作以及提供所需的参数。这样,应用可以定制屏幕界面用预填充的信息来匹配调用的操作。例如:NEP-5 token的转移,投票等。而用户需要做的是授权/签和交易。正如之前所提到的,通用智能合约的调用并不一定适用于URI。这是既是因为安全问题也是因为可用性。URI的客户端应该100%确信他们使用的URI会达成预期的效果。将其限制在合约操作的子集可以解决大部分繁杂的问题。如果客户端想要更加安全,它可以在每个受支持的智能合约URI的子集中构建自己的白名单。任何对受支持合约URI的添加都应遵守与其他NEO改进提案一样的过程。为了添加一个受支持的智能合约URI,操作/提案应具有以下内容…1.不指定应用2.经过良好的测试3.良好的文档并很容易被客户端使用4.URI使用例子NEP-5 token 转移NEP-5token的转移是智能合约的调用并具有以下URI。neo:<address>?asset=<NEP5ScriptHash>[?amount=<amount>][?<TransactionAttributeKey>=<value>] URI Keys例子开始转移10ONT到指定地址的交易Beginneo:AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y?asset=ceab719b8baa2310f232ee0d277c061704541cfb&amount=10开始转移非指定数量的ONT到指定地址交易neo:AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y?asset=ceab719b8baa2310f232ee0d277c061704541cfb原理URI需要考虑对于用户和钱包开发者的易用性和安全隐患,尤其是在执行任意智能合约。与智能合约调用相比,我们可以使用与转移原生资产相关的URI来更轻松的实现安全保障。实现参考实现• https://github.com/O3Labs/NEP…• https://github.com/O3Labs/nep…参考以下讨论有许多与智能合约URI安全相关的论点• https://github.com/ethereum/E…来自 https://github.com/neo-projec…

December 17, 2018 · 1 min · jiezi

NEO改进协议提案7(NEP-7)

文章目录摘要动机原理详述鉴权触发器 鉴权R触发器 应用触发器 应用R触发器后兼容性实现摘要触发器是一种触发智能合约执行的机制。本NEP定义四种类型的触发器,他们是鉴权触发器、鉴权R触发器、应用触发器、应用R触发器动机提供智能合约系统的区块链应当为其上运行的智能合约提供多种触发器来使其在不同环境中运行。原理当前,Neo智能合约中有两种触发器:鉴权触发器和应用触发器这两种触发器使得智能合约能够验证交易和修改区块链的状态。但没有办法让智能合约拒绝一个交易,或在接受一个交易时修改区块链的状态。我们需要两种新的触发器来实现:鉴权R触发器和应用R触发器详述我们定义四种触发器:鉴权触发器、鉴权R触发器、应用触发器和应用触发器R鉴权触发器鉴权触发器代表合约作为鉴权函数被调用。鉴权函数能接收多个参数,并返回一个布尔值代表交易和区块的有效性如果合约由鉴权触发器触发,则将调用合约的入口函数:main(…);合约的入口函数必需能够处理这种类型的调用。鉴权R触发器鉴权R触发器代表合约作为鉴权函数被调用,因为他被指定为交易的输出。鉴权函数函数不接受任何参数,并应返回一个指示交易有效性的布尔值。如果合约由鉴权R触发器触发,则将调用合约的入口函数:main(“receiving”, new object[0]);合约的入口函数必需能够处理这种类型的调用。receiving函数需要有以下编程接口:public bool receiving()receiving函数必需在合约从一个交易接收资产时被自动调用。应用触发器应用触发器代表合约作为一个应用函数被调用。应用函数可以接收多个参数,改变区块链的状态和返回各种类型的值合约可以有任何格式的入口,但我们必需记住所有合约必需有以下入口:public byte[] main(string operation, params object[] args)函数可以在创建一个InvocationTransaction时被调用。应用R触发器应用R触发器代表函数的默认函数received 函数正在被调用,因为它被指定为交易的输出。received 函数不接受任何参数,更改区块链的状态,和返回任何类型的值。当合约被鉴权R触发器触发时,会调用入口函数:main(“received”, new object[0]);received 函数需要有以下编程接口:public byte[] received()received 函数在合约从一个交易接收资产时被自动调用。后兼容性没有实现receiving和received函数的老合约在被鉴权R触发器和应用触发器触发时会导致虚拟机的FAULT状态。所以转给老合约的交易会被拒绝且没有状态会被改变实现https://github.com/neo-projec…原文链接:https://github.com/neo-projec…

December 17, 2018 · 1 min · jiezi

NEO改进协议提案8(NEP-8)

文章目录摘要动机原理详述CALL_I CALL_E CALL_ED CALL_ET CALL_EDT向后兼容性实现摘要本NEP提议NeoVM计算栈堆栈隔离,以确保动态调用的安全性,并为将来的新功能提供支持。动机现在已经实现了NEP-4(动态调用),但由于NeoVM计算栈未被隔离,动态调用的合约可能会在运行时中打断调用者的堆栈,从而导致合约未按预期执行。另一方面,某些功能(例如异常处理)也需要实现堆栈隔离。原理我们需要一组新的指令,以便每次调用都创建一个单独的计算栈,并自动将函数的参数复制到新堆栈。函数运行完毕后,返回值将自动复制到调用者的堆栈中。这样,调用的合约对栈的修改,不会影响调用者的行为。详述我们添加了五条用于启动堆栈隔离调用的新指令:CALL_I,CALL_E,CALL_ED,CALL_ET,CALL_EDT。CALL_I指令CALL_I与老指令CALL很类似。不同之处在于CALL_I需要在其之后跟随一个操作数来表示参数的数量和返回的要拷贝的值。CALL_E指令CALL_E与老指令APPCALL很类似在用于静态调用时。不同之处在于CALL_E需要在其之后跟随一个操作数来表示参数的数量和返回的要拷贝的值。CALL_ED指令CALL_ED与老指令APPCALL很类似在用于动态调用时。不同之处在于CALL_ED需要在其之后跟随一个操作数来表示参数的数量和返回的要拷贝的值。CALL_ET指令CALL_ET与指令CALL_E很类似。不同之处在于CALL_ET会开启一个尾调用。CALL_EDT指令CALL_EDT与指令CALL_ED很类似。不同之处在于CALL_EDT会开启一个尾调用。向后兼容性所有旧合约都可以在原始指令集中正确执行,但是不建议新合约继续使用旧指令。当新合约需要动态调用时,应避免使用旧指令。实现https://github.com/neo-projec…原文链接:https://github.com/neo-projec…

December 17, 2018 · 1 min · jiezi

NEO改进协议提案6(NEP-6)

文章目录摘要动机基本原理详述钱包 Scrypt参数 账户合约 向后兼容性实现摘要本NEP描述了一个钱包标准用于钱包文件在NEO多种实现间的兼容。动机目前,不同的客户端程序生成不同的钱包文件。它们具有不同的文件格式,不同的存储方式以及不同的加密方式。用户很难在不同的客户端程序之间进行迁移,因为钱包文件的格式不同。尽管可以通过导出私钥来实现迁移,但是对于具有多个私钥的钱包来说是很麻烦的。我们需要一种通用钱包格式,允许用户安全轻松地跨所有平台迁移,而无需更改钱包文件或导出私钥。基本原理钱包标准应考虑安全性和跨平台兼容性。为了安全起见,我们要求实现使用NEP-2机制来加密或解密私钥。对于跨平台,我们使用JSON格式来描述钱包文件,以便可以在每个平台上轻松识别钱包文件的内容。详述钱包Json格式的钱包文件具有以下的基础结构:{“name”: “MyWallet”,“version”: “1.0”,“scrypt”: {},“accounts”: [],“extra”: null}name是用户对钱包文件所做的标签。version目前固定为1.0,将来用于功能性升级。scrypt是一个ScryptParameters对象,用于描述钱包私钥加密解密用的SCrypt算法的参数。accounts是一个Account对象数组,用于描述钱包中每个帐户的详细信息。extra是由客户端的实现者定义,用于存储额外的数据。该字段可以为null。Scrypt参数ScryptParameters 对象具有一下结构:{“n”: 16384,“r”: 8,“p”: 8}n是定义CPU /内存开销的参数。必须是2 ^ N的值。r是调整参数。p是调整参数(并行参数)。大的p值可以在不增加内存使用量的情况下增加SCrypt的计算成本。账户账户对象具有以下结构:{“address”: “AQLASLtT6pWbThcSCYU1biVqhMnzhTgLFq”,“label”: “MyAddress”,“isDefault”: true,“lock”: false,“key”: “6PYWB8m1bCnu5bQkRUKAwbZp2BHNvQ3BQRLbpLdTuizpyLkQPSZbtZfoxx”,“contract”: {},“extra”: null}address 是账户的base58编码地址label是用户对钱包做的标签isDefault 代表账户是否是默认找零地址lock表示帐户是否被用户锁定。客户端不能花费锁定帐户中的资金。key是帐户私钥的NEP-2格式。此字段可以为空(仅限观察地址或非标准地址)。contract是一个Contract对象,用于描述合约的细节。该字段可以为null(仅限监视地址)。extra是由客户端的实现者定义的用于存储额外数据的对象。该字段可以为null。合约合约对象有以下结构:{“script”: “21036dc4bf8f0405dcf5d12a38487b359cb4bd693357a387d74fc438ffc7757948b0ac”,“parameters”: [],“deployed”: false}script是合约的脚本代码。如果合约已经被部署在区块链上,则该值可以为null。parameters是参数对象数组,用于描述合约函数中每个参数的细节。需要更多参数对象的信息,可以查看NEP-3:NeoContractABI中的描述。向后兼容性所有旧格式的钱包必需可以轻易转换成这种新的JSON格式。如果这些钱包文件包含额外的数据,可以被存储在extra值中。实现• neo-project/neo: https://github.com/neo-projec…• CityOfZion/neon-js: https://github.com/CityOfZion…原文链接:https://github.com/neo-projec…

December 14, 2018 · 1 min · jiezi

NEO改进协议提案5(NEP-5)

文章目录摘要动机详述 方法 totalSupply name symbol decimals balanceOf transfer事件 transfer实现摘要NEP-5提案概述了NEO区块链的token标准,该标准将为系统提供token化的智能合约的通用交互机制。其定义了这种机制以及其特征的缘由。还提供了模板和示例以支持开发社区。动机随着NEO区块链的发展,智能合约的部署和调用变得越来越重要。如果没有一个标准的交互方案,系统需要为每个合同维护一个唯一的API接口,无论其于其他合约的相似程度。token化合约自身就是这一需求的主要例子,由于其主要操作机制是相同的。与这些token交互的标准方法使得整个生态系统免于为每一个部署token的智能合约维护所需基本操作的定义。详述在下面方法定义中,我们提供合约中所定义的函数方法的定义以及其调用参数方法totalSupplypublic static BigInteger totalSupply()Returns 部署在系统内该token的总数.namepublic static string name()Returns token的名称. e.g. “MyToken”.该方法每次被调用时必需返回一样的值.symbolpublic static string symbol()Returns 合约所管理的token的短字符串符号 . e.g. “MYT”. 该符号需要应该比较短小 (建议3-8个字符), 没有空白字符或换行符 ,并限制为大写拉丁字母 (26个英文字符).该方法每次被调用时必需返回一样的值.decimalspublic static byte decimals()Returns token使用的小数位数 – e.g. 8, 意味着把token数量除以100,000,000来获得它的表示值.该方法每次被调用时必需返回一样的值.balanceOfpublic static BigInteger balanceOf(byte[] account)Returns 账户的token金额.参数账户必需是一个20字节的地址。如果不是,该方法会抛出一个异常。如果该账户是个未被使用的地址,该方法会返回0transferpublic static bool transfer(byte[] from, byte[] to, BigInteger amount)从一个账户转移一定数量的token到另一个账户.参数from和to必需是20字节的地址,否则,该方法会报错。参数amount必需大于等于0.否则,该方法会报错。如果账户没有足够的支付金额,该函数会返回false.如果方法执行成功,会触发转移事件,并返回true,即使数量为0或者from和to是同一个地址函数会检查from的地址是否等于调用合约的hash.如果是,则转移会被处理;否则,函数会调用SYSCALL Neo.Runtime.CheckWitness来确认转移如果to地址是一个部署合约,函数会检查其payable标志位来决定是否把token转移到该合约。如果转移没有被处理,函数会返回false.事件transferpublic static event transfer(byte[] from, byte[] to, BigInteger amount)会在token被转移时触发,包括零值转移。一个创建新token的token合约在创建token时会触发转移事件,并将from的地址设置为null一个销毁token的token合约在销毁token时会触发转移事件,并将to的地址设置为null实现• Woolong: https://github.com/lllwvlvwll…• ICO Template: https://github.com/neo-projec…原文:https://github.com/neo-projec…

December 14, 2018 · 1 min · jiezi

NEO改进协议提案4(NEP-4)

文章目录摘要动机详述neoneo-vmneo编译器智能合约示例原理向后兼容性实现摘要此NEP提案概述了一种机制,通过该机制,智能合约能够调用直到运行时才知道的其他智能合约,而不仅限于调用在编译时定义的智能合约。为了保持智能合约与未来动态分片过程接口的能力,包括一份用于构建智能合约的提案详述会表示智能合约是否需要动态合约调用功能。动机此NEP的动机是为让智能合约(SC)的的作者能够为编译时不可知的SC提供接口。例如,操作NEP-5 token的分散交换的SC可以调用在运行时才确定的token的SC的transferFrom方法。目前,这样的SC需要对所有支持的NEP-5 token地址进行硬编码,并在添加新token时重新发布。以太坊上的许多SC都需要此功能,包括任何符合ERC223 token标准的功能。通过让SC作者能够在运行时指定SC与之交互,类似于更先进的以太坊合约中包含的功能将更容易开发和维护。值得注意的是,向NEO添加动态SC调用会影响可伸缩性。通过动态SC调用,我们不用再事先知道将调用哪些其他SC,因此VM状态的子集必须可用于执行才能成功。这使得动态分片更难实现。为了克服可扩展性的缺点,该提议在区块链上创建时为每个SC添加一个设定,以表示它是否需要动态调用功能。该设定将允许所有现存的合约和大多数未来合约在预先已知的存储上下文中执行,因此更适合于动态分片,同时还使SC更强大和更具表现力。考虑到动态调用功能的可扩展性缺陷,该NEP建议了一个需要此功能的SC的更新费用结构。下面列出了更新费用结构的样例实现。详述该提案概述了Neo项目的三个部分的变更,并提供了如何在SC中使用此变更的示例:•neo•neo-vm•neo编译器•智能合约实例下面列出的变更并非试图详尽无遗,而是概述每个库中所需的重要变更。neo为了让一份SC表示其是否能够动态调用其他SC,该NEP建议在neo.Core.ContractState对象中添加以下属性,且默认值为falsepublic bool HasDynamicInvokeHasDynamicInvoke属性为了使实现与当前的Neo协议保持互操作,HasDynamicInvoke属性将被序列化为字节标志跟在现有的HasStorage属性之后:[Flags] public enum ContractPropertyState : byte { NoProperty = 0, HasStorage = 1 << 0, HasDynamicInvoke = 1 << 1, } public class ContractState : StateBase, ICloneable<ContractState> { … public ContractPropertyState ContractProperties; public bool HasStorage => ContractProperties.HasFlag(ContractPropertyState.HasStorage) public bool HasDynamicInvoke => ContractProperties.HasFlag(ContractPropertyState.HasDynamicInvoke) … public override void Serialize(BinaryWriter writer) { base.Serialize(writer); writer.WriteVarBytes(Script); writer.WriteVarBytes(ParameterList.Cast<byte>().ToArray()); writer.Write((byte)ReturnType); writer.Write(ContractProperties); // currently is writer.Write(HasStorage) writer.WriteVarString(Name); writer.WriteVarString(CodeVersion); writer.WriteVarString(Author); writer.WriteVarString(Email); writer.WriteVarString(Description); }以下在neo.SmartContract.ApplicationEngine中的变更用于计量合约创建不同的Gas费用。没有额外附加功能的合约创建费用会被降低,而那些HasDynamicInvoke或HasStorage属性为true的合约创建会产生额外的费用。 protected virtual long GetPriceForSysCall() { // lines omitted … case “Neo.Contract.Create”: case “Neo.Contract.Migrate”: case “AntShares.Contract.Create”: case “AntShares.Contract.Migrate”: long fee = 100L; ContractState contract = PeekContractState() // this would need to be implemented if( contract.HasStorage ) { fee += 400L } if( contract.HasDynamicInvoke ) { fee += 500L; } return fee * 100000000L / ratio; neo-vm本详细提案给NEO虚拟机添加了一个新OpCode,代表相对于静态的动态AppCall的使用DYNAMICCALL = 0xFADYNAMICCALL OpCode 在neo.VM.ExecutionEngine.ExecuteOp 方法的执行也按照以下方式不同于现有的APPCALL和TAILCALL OpCodes case OpCode.APPCALL: case OpCode.TAILCALL: case OpCode.DYNAMICCALL: { if (table == null) { State |= VMState.FAULT; return; } byte[] script_hash = null; if ( opcode == OpCode.DYNAMICCALL ) { script_hash = EvaluationStack.Pop().GetByteArray(); if ( script_hash.Length != 20 ) { State |= VMState.FAULT return; } } else { script_hash = context.OpReader.ReadBytes(20); } byte[] script = table.GetScript(script_hash); if (script == null) { State |= VMState.FAULT; return; } if (opcode == OpCode.TAILCALL || opcode == OpCode.DYNAMICCALL) InvocationStack.Pop().Dispose(); LoadScript(script); } break;neo编译器将方法调用转换为DYNAMICCALL的示例方法如下: else if (calltype == CallType.DYNAMICCALL) { _ConvertPush(callhash, null, to) _Convert1by1(VM.OpCode.DYNAMICCALL, null, to); }智能合约示例以下是一个SC演示了所提案功能的简单使用using Neo.SmartContract.Framework.Services.Neo;namespace Neo.SmartContract{ public class DynamicTotalSupply : Framework.SmartContract { public static int Main(byte[] contract_hash) { if( contract_hash.Length == 20 ) { BigInteger totalSupply = DynamicCall( contract_hash, ’totalSupply’) return totalSupply; } return 0; } }}原理用动态SC调用来动态分片(以太坊已经提出了许多解决方案)并不是不可能的,尽管这会增添已经很困难的任务。仅仅因为我们事先知道计算调用图并不意味着我们能够成功地完美分配资源,没有重叠的子集。仍然可能需要实现分片之间的通信,如以太坊提案中那样。考虑到这一点,不向SC添加任何关于是否需要动态调用的元数据并实现动态应用程序调用是有可能的,因为它们可以以相同的方式执行和延申。但是,即使在可以实现一个系统可以动态SC调用和动态分片这种情况下,本提案仍认为存储HasDynamicInvoke属性在该实现中可能是很有用的。存储此属性还能使系统对使用HasDynamicInvoke属性发布的SC收取不同的费用。向后兼容性本NEP介绍了一套不影响现有SC的新功能。通过利用现有字节来指示SC是否需要存储区或添加额外标记,我们能够在不影响现有网络协议的情况下维持现有功能和添加该新功能。实现• neo-project/neo: https://github.com/neo-projec…• neo-project/neo-vm: https://github.com/neo-projec…原文链接: https://github.com/neo-projec… ...

December 14, 2018 · 2 min · jiezi

NEO改进协议提案3(NEP-3)

文章目录• 摘要• 动机• 原理• 详述• 合约• Function• Event• Parameter• ParameterType• EntryPoint摘要应用程序二进制接口(ABI)是两个程序模块之间的接口,其中一个通常是库和/或操作系统,另一个通常是由程序员创建的应用程序。本NEP描述了NEO智能合约的ABI标准动机NEO智能合约系统旨在在合同之间相互调用。为实现这一目标,我们需要一种机制来公开智能合约的接口。使用NeoContract ABI,开发人员可以轻松的创建程序来调用智能合约或编写能自动访问合同功能的客户端。原理我们假设应用程序二进制接口(ABI)是强类型的,在编译时已知且静态。其不会提供内省机制。我们主张所有合同都具有其在编译时可以调用的合约的接口定义此详述不涉及其接口是动态的或仅在运行时可知的合约。如果这些案例十分重要,可以适当的提出作为特殊构建在NEO的生态体系中。详述合约NeoContract ABI以Json格式定义定义,包含以下结构体,并且某些顶层对象可以包含数个子对象:{“hash”: “0x562851057d8afbc08fabc8c438d7cc771aef2195”,“entrypoint”: “main”,“functions”: [],“events”: []}hash是合约的脚本哈希。它采用16进制字符按大端序编码Entrypoint 代表合约函数的入口functions 是一个函数对象数组,用于描述合约中每个函数的细节events 是一个事件对象数组,用于描述合约中没给事件的细节Function函数对象包含以下结构:{“name”: “transfer”,“parameters”: [],“returntype”: “Boolean”}name代表函数的名称,可以是任何有效的标识符。parameters是一个参数对象数组,用于描述函数中每个参数的详细信息returntype代表函数的返回类型。可以是以下任意一种值:Signature, Boolean, Integer, Hash160, Hash256, ByteArray, PublicKey, String, Array, InteropInterfaceEvent事件对象包含以下结构体{“name”: “refund”,“parameters”: []}name代表事件的名称,可以是任何有效的标识符。parameters是一个参数对象数组,用于描述事件中每个参数的详细信息Parameter参数对象包含以下结构体:{“name”: “from”,“type”: “Hash160”}name代表参数的名称,可以是任意有效字符type代表参数类型。可以是以下任意一种值:Signature, Boolean, Integer, Hash160, Hash256, ByteArray, PublicKey, String, Array, InteropInterfaceParameterType参数类型有以下值:名称 描述Signature A signature of a transaction or block which is generated by the user.Boolean 布尔值的值是true或者false.Integer An arbitrarily large integer whose value in theory has no upper or lower bounds.Hash160 160比特的整型.Hash256 256比特的整型.ByteArray 字节数组.PublicKey 采用压缩模式的ECC公钥.String 采用UTF-8编码的字符串.Array 对象数组. 其元素类型可以是参数类型中的任何一种.InteropInterface 返回互操作服务的接口.Void Void 意味着函数没有返回值. 该值不是参数类型中的任何一种.EntryPoint强烈建议每个合约具有以下入口函数:{“name”: “main”,“parameters”: [{“name”: “operation”,“type”: “String”},{“name”: “args”,“type”: “Array”}],“returntype”: “ByteArray”}通过这种方式,调用者可以从入口点轻松的访问函数,通过第一个参数指定调用函数的名称,通过第二个参数指定函数的的参数原文链接:https://github.com/neo-projec… ...

December 13, 2018 · 1 min · jiezi

NEO改进协议提案1(NEP-1)

文章目录什么是NEPNEP基本原理NEP类型NEP工作流程怎么才是一个合格的NEPNEP格式和模板NEP序言 附件NEP所有权转让NEP编辑者NEP编辑者的职责和工作流程历史什么是NEPNEP是NEO改进协议。一份NEP是一份设计文档用于给给NEO社区提供信息,或是描述一个NEO的新特性或其工序或环境。NEP需要对特性提供一份简要的技术说明以及基本原理。NEP的作者有责任在社区内构建舆论和编辑不同的观点NEP基本原理我们计划NEP的主要作用是提出新特性,收集社区中关于某个问题的观点和整理归纳引进到NEO中的设计决定。由于NEP作为版本文件存储在版本化的存储库中,它们的版本历史是特性提案的历史记录。对于NEO的实施者,NEP是一种便捷的方式来追踪他们的实施进度。理想情况下,每个实施维护人员都会列出他们的NEP。如此会使终端使用者很方便的了解某个实现或者库的状态。NEP类型有三种类型的NEP:·一份标准路线 NEP描述任何会影响多数NEO实施者的影响,例如一个网络协议的改变,一个区块或者交易有效性规则的改变,拟议应用标准/公约,或者任何会影响应用使用NEO操作性的改变或者添加·一份信息类 NEP描述一个NEO的设计问题,或提供指给社区指南或者信息,但是并不提议一个新特性。信息类的NEP并不必然代表一个NEO社区的共识或者推荐,所以使用者或者实施者可以自由的忽略信息类的NEP或跟随建议·一份元NEP描述了一个围绕NEO的工序流程,或者提出了一个工序流程(或事项目)的改变。元NEPS类似于标准路线NEP,但适用于除NEO协议本身之外的区域。他们可能提出一个实现,但不提到NEO的代码库;他们需要社区的共识;与信息类NEP不同,它们不仅仅是建议,而且用户通常不能自由地忽略它们。示例包括流程、指南、决策过程的更改,以及NEO开发中使用的工具或环境的更改。NEP工作流程一个NEP的流程开始于一个关于NEO的新想法。强烈建议一个单一的NEP包含一个单一的关键流程或新想法。多关注NEP就越容易成功。对于单一客户端的变动不需要一个NEP,但可能影响多客户端的改变或者定义多个app应用标准则需要。NEP编辑者有权拒绝NEP提案,如果它们显得过于不集中或过于宽泛。如果有疑问,把你的NEP分成几个比较集中的。每个NEP必需有个拥护者—使用以下描述的风格和格式编写NEP的人,在的论坛中适当的指导讨论,并试图围绕这个想法建立社区共识。在写一个NEP前先公开审查一下想法意味着节约了作者的潜在时间。先向NEO社区询问一个想法是否具有原创性有助于防止花费太多时间在基于先前讨论而保证被否定的事情上(搜索因特网并不总是奏效)。它也有助于确定这个想法适用于整个社区,而不仅仅是作者。仅仅因为一个想法对作者来说听起来不错,并不意味着它对使用NEO的各领域的大多数人都有效。通过合适的论坛来评估NEP,包括NEO子版、仓库的问题部分和NEO闲置通道之一。特别的,仓库的问题部分非常适合与社区讨论你的提议并开始创建一些有有关于你的NEP的正式言论。一旦拥护者向近NEO社区询问一个想法是否有任何机会被接纳为NEP草案这给了作者一个连续编辑NEP草稿的机会,用于正确的格式和质量。这也允许进一步的公众评论和NEP的作者来关注这个提案。如果NEP的协作者同意,NEP编辑者会给NEP分配一个数字,标记它是标准、信息、或是元,并给它状态‘草案’,并将它加入到git仓库。NEP编辑者不会不合理的否定一个NEP。否定NEP的理由包括重复劳动、技术不健全、不正当动机或向后兼容,或违背NEO的价值观标准追踪型NEP由三部分组成,设计文档、实现,最后如果需要更新的正式规范。在实施开始之前,NEP需要被审核和采纳,除非该实施将有助于人们研究NEP。标准追踪型NEP必须包含一个实现——以代码、补丁或URL的形式——在其被认定结束状态前。对于一个被接受的NEP,它必须满足一定的最低标准。所提出的改善提案必须是一个清晰和完整的描述。改善必须是一个纯的改进。提案实现,如果适用的话,必须是可靠的并且不能过分复杂化协议。一旦NEP被采纳,就必须完成实现。当实现完成并被社区采纳时,状态将改为“结束”。NEP也可以被赋予“延期”的状态。NEP作者或编辑者可以在NEP没有进展的情况下给NEP分配该状态。一旦NEP被推迟,NEP编辑者可以重新将其分配成草稿状态。NEP也可以被“拒绝”。也许这不是个好主意。记录这一事实仍然很重要。NEP也可以被一个不同的NEP替代,使原来的过期。NEP状况的可能路径如下:一些信息型或元NEP也可能是状态“活跃”如果他们从未被完成,例如NEP1(本NEP)。怎么才是一个合格的NEP每个NEP应该有以下部分:·序言——RFC 822样式标头,包含关于NEP的元数据,包括NEP编号、简短的描述性标题(限制最多44个字符)、姓名、以及可选的每个作者的联系人信息等。·摘要——-一个简短的(200字)描述正在处理的技术问题。·动机(可选)-动机是那些想要改变NEO协议的NEP至关重要的部分。它应该清楚地解释现有的协议规范的不足以及NEP解决的问题。没有充分动机的NEP提案可能被彻底拒绝。·详述——技术详述应该描述新特征的语法和语义。该规范应该足够详细,以允许针对任何当前NEO平台的竞争、可互操作的实现。•基本原理——基本原理详细说明设计目的以及设计方案的理由。它应该描述相关工作的替代设计,例如在其他语言中如何支持该特性。基本原理也可以提供社区内共同意见的证据,并且应当讨论在讨论期间提出的重要反对或重点。·向后兼容性——引入向后兼容性的所有NEP必须包括描述其不兼容性及其严重性。NEP必须解释作者是如何处理这些不兼容性的。没有足够的向后兼容性的NEP提交可能被彻底拒绝。·测试用例——实现的测试用例对于那些会引起共识改变的NEP是必须的。其他NEP可以选择包括测试用例的链接如果需要的化。·实现——实现必须在任何NEP“完结”状态前之前完成,但是不需要在NEP受理前完成。最好先完成规范和原理并在编写代码之前达成共识。NEP格式和模板NEP必需用 mediawiki or markdown格式编写。图片文件必需包含在NEP的子目录。NEP序言每个NEP必须由一个RFC822格式的头部栏开始。头部栏必须包含以下顺序。用号标示的是可选的,稍后写介绍。其他都是必须的。NEP: <NEP编号>(由NEP编辑者决定)Title: <NEP标题>Author: <list of authors’ real names and optionally, email address>*Discussions-To: <email地址>Status: <Draft | Active | Accepted | Deferred | Rejected | Withdrawn |Final | Superseded>Type: <Standard | Informational | Meta>Created: <date created on, in ISO 8601 (yyyy-mm-dd) format>*Replaces: <NEP编号>*Superseded-By: <NEP编号>*Resolution:作者头部栏列出NEP的所有作者/所有者的姓名,以及可选的电子邮件地址。作者头值的格式必须是 Random J. User address@dom.ain有email地址的情况下,Random J. User 没有email地址的情况下。如果有多个作者,每一个都应在之后独立的一行中的遵守RFC2822的协议。注意:解决方案栏只适用于标准追踪型NEP。它包含一个URL,该URL应该指向一个电子邮件消息或其他关于NEP的声明的Web资源。当NEP处于私下讨论阶段时(通常在初始草稿阶段),Discussions-To栏将指示正在讨论NEP的邮件列表或URL。如果NEP处于与作者私下讨论阶段,则不需Discussions-To栏。类型栏指定NEP的类型:标准、信息或元。创建栏记录了NEP被分配编号的日期。它应该是YYYY-MM-DD格式,例如2001-08-14。NEPS可能有一个需求栏,指示NEP依赖的NEP编号。NEP还可以有一个Superseded-By栏,指示NEP已经被后面的文档淘汰;该值是替换当前文档的NEP文档编号。较新的NEP必须有一个替换栏,该栏包含其过时的NEP编号。附件NEP可以包括附件,如图表。此类文件必须包含在该NEP的子目录中,并命名为nep-x-y.ext,其中“x”是NEP编号,“y”是序列号(从1开始),而“ext”被实际的文件扩展名(例如“png”)替换。NEP所有权转让有时候需要将NEP所有权转让给新的拥护者。一般来说,我们希望保留原作者作为已转移NEP的合著者,但这取决于原作者。转移所有权的一个恰当的理由是,因为原始作者不再有时间或兴趣更新它,或者继续执行NEP的流程,或者已经脱离“网络”的位面(即,无法访问或不回复电子邮件)。转移所有权的一个不恰当的原因是因为你不同意NEP的方向。我们试图在围绕NEP建立共识,但如果这是不可能的,你可以提交一个竞争的NEP。如果您有兴趣接管NEP的所有权,请向原始作者和NEP编辑者发送请求接管的消息。如果原作者没有及时回复邮件,NEP编辑者会做出单方面的决定(此类决定并非不能逆转:).NEP编辑者当前的NEP编辑者是·Erik Zhang (@erikzhang)NEP编辑者的职责和工作流程每收到一份新的NEP,编辑者会做如下事情:·阅读NEP检查它是否完备:健全和完整。想法必需有技术意义,即使它看起来并不能被接受。·标题必需准确的描述内容。·编辑NEP的语言(拼写,语法,句子结构等),标记,代码风格。如果NEP并不完备,编辑者会将其退回给作者重新修订,并给出具体说明一旦NEP准备好合到仓库,NEP编辑者会:·分配一个NEP编号(基本是下一个可用的数字,但有时也可能是一个特殊数字,例如666或者3141)在拉取请求的评论中.·当作者准备好后合并下拉请求(允许有进一步的同行评审时间).·在README.mediawiki中列出NEP.·回复NEP作者告知下一步操作.NEP编辑者旨在履行管理和编辑的职责.NEP编辑者收集NEP的变化,并改正任何我们看到的结构、语法、拼写或标记上的额错误。历史本文档是根据Amir Taaki从Python版PEP-0001衍生出的比特币的BIP-0001文档编写的。在许多地方仅是简单复制和修改。虽然PEP-0001文档是由Barry Warsaw, Jeremy Hylton, and David Goodger编写,但是他们并不负责其在NEO改善过程中的使用,并且不用回答任何NEO或者NEP的技术问题。请把所有意见评论直接提交给NEP编辑者。原文:来自 https://github.com/neo-projec…

December 13, 2018 · 1 min · jiezi

NEO改进协议提案2(NEP-2)

文章目录摘要动机基本原理详述前缀 建议详述 加密步骤 解密步骤向后兼容性测试用例实现摘要提出了一种以58字符 Base58Check编码的可打印字符串的形式对密码保密型私钥记录进行加密和编码的方法。加密私钥记录旨在用于纸质钱包。每一个记录字符串都包含除了密码之外重构私钥所需的所有信息,并且该方法使用加盐和scrypt来抵抗暴力攻击。动机密码和密码保护型密钥是个人间发送资产的新实践。想要发送资产的人可以通过邮政邮件邮寄一个受密码保护的纸钱包并通过电话或者email给接收者密码,以保证传递的安全不受其他通道的拦截。纸钱包的使用者可以携带资金的加密私钥并在家中留存一份拷贝以预防意外的遗失和偷窃。把资产存在银行账户或者保险箱中的纸钱包的用户可以把密码留在家里或者与可信的伙伴分享已预防银行中的某人乘机访问纸钱包并花费其中的资产。预见和不可预见的关于密码保护型私钥的用例还有很多。另一方面,标准的密码保护型私钥格式使得其能够共享来自不同钱包客户端的私钥。基本原理用例:作为一个NEO纸钱包用户,我喜欢添加加密的功能,如此我的NEO纸钱包可以拆解成两个因子:我有的和我知道的。用例:作为一个NEO用户想要用一个私钥向个人或者公司付款,我不必担心任何通信渠道会拦截我的key和导致资产的被盗。我想要提供一个密码保护型密钥,伴随一个可以通过其他渠道传输的密码。详述本提议运用到了以下函数和定义:·AES256加密、AES256解密,是众所周知的AES分组编码不考虑初始向量和块连接的简单格式。这些函数每一个都需要一个256位的key和16字节的输入和确定的16字节的输出。·SHA256,一种众所周知的任意字节长度输入和固定产出32字节长度hash的哈希算法。·scrypt,一个著名的密钥推导算法。它需要以下参数(string) password, (string) salt, (int) n, (int) r, (int) p, (int) length,并固定产生一个长度等于length长度的字节数组。Base58Check,一个在NEO系统中广泛使用的用58的字符byte[]编码的方法。前缀建议将Base58Check编码过后的字符串以6字开头。数字6旨在从用户的角度表示一种需要其他东西才能生效的私钥–总的定义可以理解为将来包括特别是在多签交易中的密钥,并且被选遵从现有前缀5,多见于WIF格式表示一种非加密私钥。建议第二个字符应该给出提示所需什么因子,对于加密密钥需要一个密码,建议使用大写字母P。为保持加密密钥的大小,在AES加密中不使用初始向量(IVS)。相反,使用scrypt从密码以及NEO地址的32位hash作为盐导出用于类似初始向量用途的合适值。建议详述对象标识符前缀:0x0142。这是在Base58Check编码记录开头的常量字节,它们的存在导致结果字符串具有可预见的前缀。用户如何判断:58个字符总是以“6P”开头有效载荷字节数(不包括前缀):37·1字节(FLAG字节):始终为0xE0·4字节:Sha256(Sha256(expected_neo_address))[0…3]),用于拼写检查和加盐。16字节:AES加密密钥材料记录(加密半数1)16字节:AES加密密钥材料记录(加密半数2)在BASE58CHECK编码(前缀6Py)中的范围:最小值:6PYJXKPVNKXUZAFD2B5ZSZAFJYNP4EZQQECJS39 449 QUUXLNXJLX6LG(基于01 42 E0加三十六个00)最大值:6PyxG5TnGyLyxDrZiqxBuxxdotBNTBI3D61MQBXPPZQZEJTVQQHSCNK(基于01 42 E0加三十六个FF)加密步骤1、计算NEO地址(ASCII),并获取SHA256(SHA256())的前四个字节.我们将其叫做地址哈希。2.使用Scrypt从密码导出一个密钥。·参数:密码是以UTF-8格式的密码。盐采用之前得到的地址hash,n=16384,r=8,p=8,length=64·把结果的64字节分成2半,称他们为导出半数1和导出半数2.3.做AES256加密(block = privkey[0…15] xor derivedhalf1[0…15], key = derivedhalf2),把16字节的结果叫做加密半数1.4.做AES256加密(block = privkey[16…31] xor derivedhalf1[16…31], key = derivedhalf2),把16字节的结果叫做加密半数2.加密私钥是以下Base58Check编码下的串联,总共39字节,没有Base58 checksum:·0x01 0x42 + flagbyte + addresshash + encryptedhalf1 + encryptedhalf2解密步骤1.从用户那获取加密私钥和密码。2.把密码和地址hash带入Scrypt函数得到半导出数1和半导出数2.3.用AES256Decrypt解密半导出数1和半导出数2,并合并两部分并将结果和半导出数1做异或得到明文形式私钥。4.把明文私钥转化成NEO地址。5.求NEO地址hash,并验证加密私钥记录中的地址hash是否与之匹配。如果不是,则报告密码错误。向后兼容性向后兼容性是最小的,由于它是一种新标准几乎扩展了WIF格式。假设私钥数据的入口可以接受现存格式的私钥(比如16进制数或者WIF格式);本草案使用的密钥格式不能被错误地用于任何现有的格式,并保留自动检测能力。测试用例Test 1:• Passphrase: TestingOneTwoThree• Encrypted: 6PYVPVe1fQznphjbUxXP9KZJqPMVnVwCx5s5pr5axRJ8uHkMtZg97eT5kL• Unencrypted (WIF): L44B5gGEpqEDRS9vVPz7QT35jcBG2r3CZwSwQ4fCewXAhAhqGVpP• Unencrypted (hex): CBF4B9F70470856BB4F40F80B87EDB90865997FFEE6DF315AB166D713AF433A5Test 2:• Passphrase: Satoshi• Encrypted: 6PYN6mjwYfjPUuYT3Exajvx25UddFVLpCw4bMsmtLdnKwZ9t1Mi3CfKe8S• Unencrypted (WIF): KwYgW8gcxj1JWJXhPSu4Fqwzfhp5Yfi42mdYmMa4XqK7NJxXUSK7• Unencrypted (hex): 09C2686880095B1A4C249EE3AC4EEA8A014F11E6F986D0B5025AC1F39AFBD9AE实现• neo-project/neo: https://github.com/neo-projec…• CityOfZion/neon-js: https://github.com/CityOfZion…原文:来自 https://github.com/neo-projec… ...

December 13, 2018 · 1 min · jiezi

一个开源vue网站博客,nuxt开源网站,前后端分离项目

unNue.com开媛笔记,基于nuxt ssr首屏服务器端渲染 。用于分享、记录、交流和学习,希望可以帮助到小伙伴们。同时网站在不断更新,创造属于猿(媛)的世界 -$Bao Yalong ..Let’s Go! https://unnue.com简述前端 Github地址语言:Javascript主框架:Nuxt状态管理:Vuex路由:vue-router网络请求:axios编辑器:markdown-it代码高亮:highlight.js日期处理:moment头像:gravatar第三方登录:QQ服务端 ~~ 即将开源语言:Typescript主框架:Nestjs数据库:Mysql数据库ORM:typeorm静态资源:七牛云CDN:七牛云后台管理 ~~ 暂未开源语言:Javascript前端框架:Vue后台管理UI:Element服务器系统:CentOS运行环境:Nodejs反向代理:Nginx进程管理:Pm2项目管理:Git关于服务器的部署查阅这篇文章 CentOS从零开始部署Nodejs项目预览前台后台管理最终效果: https://unnue.com

December 13, 2018 · 1 min · jiezi

webpack 入门与解析

每次学新东西总感觉自己是不是变笨了,看了几个博客,试着试着就跑不下去,无奈只有去看官方文档。 webpack是基于node的。先安装最新的node。1.初始化安装node后,新建一个目录,比如html5。cmd中切到当前文件夹。npm init -y这个命令会创建一个默认的package.json。它包含了项目的一些配置参数,通过它可以进行初始安装。详细参数:https://docs.npmjs.com/files/package.json。不要y参数的话,会在命令框中设置各项参数,但觉得没啥必要。2.安装webpacknpm install webpack --save-dev将webpack安装到当前目录。虽然npm install webpack -g 可以讲webpack安装到全局,但是容易出现一些模块找不到的错误,所以最好还是安装到当前目录下。3.目录结构webpack是一款模块加载各种资源并打包的工具。所以先建一个如下的目录结构:app包含的开发中的js文件,一个组件,一个入口。build中就是用来存放打包之后的文件的。webpack.config.js 顾名思义用来配置webpack的。package.json就不用说了。component.jsexport default function () {``var element = document.createElement(``'h1'``);``element.innerHTML = 'Hello world'``;``return element;``}component.js 是输出一个内容为h1元素。export default 是ES6语法,表示指定默认输出。import的时候不用带大括号。index.jsimport component from './component'``;``document.body.appendChild(component());index.js 的作用就是引用Component模块,并在页面上输出一个h1元素。但完成这个还需要一个插件,因为目前我们还没有index.html文件。npm install html-webpack-plugin --save-devhtml-webpack-plugin的用来生成html,将其也安装到开发目录下面。4.设置 webpack 配置文件我们需要通过webpack.config.js文件告诉webpack如何开始。配置文件至少需要一个入口和一个输出。多个页面就需要多个入口。node的path模块const path = require(``'path'``);``const HtmlWebpackPlugin = require(``'html-webpack-plugin'``);``const PATHS = {``app: path.join(__dirname, 'app'``),``build: path.join(__dirname, 'build'``),``};``module.exports = {``entry: {``app: PATHS.app,``},``output: {``path: PATHS.build,``filename: '[name].js'``,``},``plugins: [``new HtmlWebpackPlugin({``title: 'Webpack demo'``,``}),``],``};第一次看到这个配置文件是有点懵,主要是exports,分三个部分,一个入口,一个输出,一个插件。入口指向了app文件夹。默认会把包含"index.js"的文件作为入口。输出指定了build地址和一个文件名;[name]这儿表示占位符,可以看成webpack提供的一个变量。这个具体后面再看。而HtmlWebpackPlugin会生成一个默认的html文件。5.打包有了以上准备,直接输入 webpack 就能运行了。这个输出包含了Hash(每次打包值都不同),Version,Time(耗时)。以及输出的文件信息。这时打开build文件夹,发现多了一个app.js和index.html文件,双击index.html:也可以修改下package.json?{``"name"``: "Html5"``,``"version"``: "1.0.0"``,``"description"``: ""``,``"main"``: "index.js"``,``"scripts"``: {``"build"``: "webpack"``},``"keywords"``: [],``"author"``: ""``,``"license"``: "ISC"``,``"devDependencies"``: {``"html-webpack-plugin"``: "^2.28.0"``,``"webpack"``: "^2.2.1"``}``}指定build。在cmd中执行npm run build 得到同样的结果出现helloword。再看下文件内容index.html:&lt;!DOCTYPE html&gt;``&lt;``html``&gt;``&lt;``head``&gt;``&lt;``meta charset``=``"UTF-8"``&gt;``&lt;``title``&gt;Webpack demo&lt;/``title``&gt;``&lt;/``head``&gt;``&lt;``body``&gt;``&lt;``script type``=``"text/javascript" src``=``"app.js"``&gt;&lt;/``script``&gt;&lt;/``body``&gt;``&lt;/``html``&gt;默认引用了app.js。6、解析app.js/******/ (``function``(modules) { // webpackBootstrap``/******/ // The module cache``/******/ var installedModules = {};``/******/ // The require function``/******/ function __webpack_require__(moduleId) {``/*****/ // Check if module is in cache``/******/ if``(installedModules[moduleId])``/******/ return installedModules[moduleId].exports;``/******/ // Create a new module (and put it into the cache)``/******/ var module = installedModules[moduleId] = {``/******/ i: moduleId,``/******/ l: false``,``/******/ exports: {}``/******/ };``/******/ // Execute the module function``/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);``/******/ // Flag the module as loaded``/******/ module.l = true``;``/******/ // Return the exports of the module``/******/ return module.exports;``/******/ }``/******/ // expose the modules object (__webpack_modules__)``/******/ __webpack_require__.m = modules;``/******/ // expose the module cache``/******/ __webpack_require__.c = installedModules;``/******/ // identity function for calling harmony imports with the correct context``/******/ __webpack_require__.i = function``(value) { return value; };``/******/ // define getter function for harmony exports``/******/ __webpack_require__.d = function``(exports, name, getter) {``/******/ if``(!__webpack_require__.o(exports, name)) {``/******/ Object.defineProperty(exports, name, {``/******/ configurable: false``,``/******/ enumerable: true``,``/******/ get: getter``/******/ });``/******/ }``/******/ };``/******/ // getDefaultExport function for compatibility with non-harmony modules``/******/ __webpack_require__.n = function``(module) {``/******/ var getter = module &amp;&amp; module.__esModule ?``/******/ function getDefault() { return module[``'default'``]; } :``/******/ function getModuleExports() { return module; };``/******/ __webpack_require__.d(getter, 'a'``, getter);``/******/ return getter;``/******/ };``/******/ // Object.prototype.hasOwnProperty.call``/******/ __webpack_require__.o = function``(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };``/******/ // __webpack_public_path__``/******/ __webpack_require__.p = ""``;``/******/ // Load entry module and return exports``/******/ return __webpack_require__(__webpack_require__.s = 1);``/******/ })``/************************************************************************/``/******/ ([``/* 0 */``/***/ (``function``(module, __webpack_exports__, __webpack_require__) {``"use strict"``;``/* harmony default export */ __webpack_exports__[``"a"``] = function () {``var element = document.createElement(``'h1'``);``element.innerHTML = 'Hello world'``;``return element;};/***/` `}),/* 1 //***/` `(function(module, __webpack_exports__, __webpack_require__) {“use strict”;Object.defineProperty(webpack_exports, "__esModule", { value:` `true` `});/ harmony import / var WEBPACK_IMPORTED_MODULE_0__component = webpack_require(0);document.body.appendChild(__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__component__[“a” / default /])());// })/******/` `]);`而app.js内容比较多了。整体是一个匿名函数。`(function(module) {})([(function` `(){}),` `function() {}])app文件夹中的两个js文件成了这儿的两个模块。函数最开始是从__webpack_require__开始return webpack_require(webpack_require.s = 1);这里指定从模块1执行(赋值语句的返回值为其值)。而模块1的调用是通过__webpack_require__的这句执行的。&lt;u&gt;复制代码&lt;/u&gt; 代码如下:modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);通过call调用模块的主要作用是为了把参数传过去。(function(module, webpack_exports, webpack_require) {"use strict";Object.defineProperty(__webpack_exports__,` `"__esModule", { value: true });/* harmony import */` `var` `__WEBPACK_IMPORTED_MODULE_0__component__ = __webpack_require__(0);document.body.appendChild(webpack_require.i(WEBPACK_IMPORTED_MODULE_0__component["a"` `/* default */])());/***/` `})`webpack_require 每加载一个模块都会先去模块缓存中找,没有就新建一个module对象:`var` `module = installedModules[moduleId] = {i: moduleId,l:` `false,exports: {}};模块1中加载了模块0,var WEBPACK_IMPORTED_MODULE_0__component = webpack_require(0);WEBPACK_IMPORTED_MODULE_0__component 返回的是这个模块0的exports部分。而之前Component.js的默认方法定义成了webpack_exports["a"] = function () {var` `element = document.createElement(‘h1’);element.innerHTML = ‘Hello world’;return element;}`所以再模块1的定义通过"a“来获取这个方法:&lt;u&gt;复制代码&lt;/u&gt; 代码如下:document.body.appendChild(__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__component__["a" /* default */])());这样就完整了,但这里使用了__webpack_require__.i 将原值返回。`/******/`&nbsp; `// identity function for calling harmony imports with the correct context/****/&nbsp; webpack_require.i = function``(value) { return value; };`不太明白这个i函数有什么作用。这个注释也不太明白,路过的大神希望可以指点下。小结:webpack通过一个立即执行的匿名函数将各个开发模块作为参数初始化,每个js文件(module)对应一个编号,每个js中export的方法或者对象有各自指定的关键字。通过这种方式将所有的模块和接口方法管理起来。然后先加载最后的一个模块(应该是引用别的模块的模块),这样进而去触发别的模块的加载,使整个js运行起来。到这基本了解了webpack的功能和部分原理,但略显复杂,且没有感受到有多大的好处。继续探索。 ...

December 9, 2018 · 2 min · jiezi

十大热门的JavaScript框架和库

JavaScript 框架和库可以说是开源项目中最庞大也是最累的类目了,目前在github 上这一类的项目是最多的,并且几乎每隔一段时间就会出现一个新的项目席卷网络社区,虽然这样推动了创新的发展,但不得不说苦了前端的开发者们。因此本文罗列出了一些优秀的 Javascript 框架和库的特及其在 github 上的 star 数,旨在为各位开发者提供一些参考。1、ReactJS(Star: 59989,Fork: 10992)主页:了解更多React.js(React)是一个用来构建用户界面的 JavaScript 库,主要用于构建UI,很多人认为 React 是 MVC 中的 V(视图)。React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源。React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。React 特点: 1.声明式设计−React采用声明范式,可以轻松描述应用。 2.高效−React通过对DOM的模拟,最大限度地减少与DOM的交互。 3.灵活−React可以与已知的库或框架很好地配合。 4.JSX− JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。 5.组件− 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。 6.单向响应的数据流− React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。2、AngularJS(Star: 54769,Fork: 27292)主页:https://angularjs.orgAngular JS (Angular.JS) 是一组用来开发 Web 页面的框架、模板以及数据绑定和丰富 UI 组件。它支持整个开发进程,提供 Web 应用的架构,无需进行手工 DOM 操作。 AngularJS 很小,只有 60K,兼容主流浏览器,与 jQuery 配合良好。3、Vue.js(Star: 43608, Fork: 5493)https://cn.vuejs.org/Vue.js 是构建 Web 界面的 JavaScript 库,提供数据驱动的组件,还有简单灵活的 API,使得 MVVM 更简单。主要特性: ●可扩展的数据绑定 ●将普通的 JS 对象作为 model ●简洁明了的 API ●组件化 UI 构建 ●配合别的库使用4、jQuery(Star: 43432, Fork: 12117)主页:https://jquery.com/JQuery 是轻量级的js库(压缩后只有21k) ,它兼容CSS3,还兼容各种浏览器 (IE 6.0+, FF 1.5+, Safari 2.0+, Opera 9.0+)。jQuery使用户能更方便地处理HTML documents、events、实现动画效果,并且方便地为网站提供AJAX交互。jQuery还有一个比较大的优势是,它的文档说明很全,而且各种 应用也说得很详细,同时还有许多成熟的插件可供选择。jQuery能够使用户的html页保持代码和html内容分离,也就是说,不用再在html里面插入一堆js来调用命令了,只需定义id即可。5、Meteor(Star: 36691,Fork: 4617)主页:http://www.meteor.comMeteor 是一组新的技术用于构建高质量的 Web 应用,提供很多现成的包,可直接在浏览器或者云平台中运行。6、Angular2(Star:20803,Fork:5367)主页:https://angular.ioAngular 是一款十分流行且好用的 Web 前端框架,目前由 Google 维护。这个条目收录的是 Angular 2 及其后面的版本。由于官方已将 Angular 2 和之前的版本Angular.js分开维护(两者的 GitHub 地址和项目主页皆不相同),所以就有了这个页面。7、Ember.js(Star: 17540,Fork: 3646)主页:http://emberjs.comEmber是一个雄心勃勃的Web应用程序,消除了样板,并提供了一个标准的应用程序架构的JavaScript框架。8、Polymer(Star:16979,Fork: 1699)主页:http://www.polymer-project.org在2013年的Google I/O大会上,Google发布了Polymer,它是一个使用Web组件构建Web应用的类库,同时也使用了为Web构建可重用组件的新的HTML 5标准。Polymer为大部分Web组件技术提供了polyfills功能,它能让开发者在所有的浏览器支持新特性前创建自己的可重用组件。此外,Polymer提供了一系列的部件的例子,其中包括天气、时钟、股票行情和线型图。Polymer中的polyfills为需要使用Web组件成功构建应用提供了多种Web技术,包括: ●HTML imports:种在其他HTML document中引入和重用HTML document的方法。 ●自定义元素:让开发者定义和使用自定义DOM元素。 ●Shadow DOM:在DOM中提供的封装。 ●模型驱动视图(Model Driven Views):提供象AngularJS的数据绑定。 ●Web动画:实现复杂动画的API。 ●Pointer事件:对鼠标触摸和手写笔事件的封装9、Zepto.js(Star: 12074,Fork: 3260)主页:https://facebook.github.io/react Zepto.js 是支持移动WebKit浏览器的JavaScript框架,具有与jQuery兼容的语法。2-5k的库,通过不错的API处理绝大多数的基本工作。10、Riot.js(Star: 11491,Fork: 902)主页:http://riotjs.comRiot.js是一个客户端模型-视图-呈现(MVP)框架并且它非常轻量级甚至小于1kb.尽管他的大小令人难以置信,所有它能构建的有如下:一个模板引擎,路由,甚至是库和一个严格的并具有组织的MVP模式。当模型数据变化时视图也会自动更新。当然除了以上提到的这些,还有很多优秀的 Javascript 框架和库,并且几乎每隔一段时间就会涌现一个新的产品。 ...

December 9, 2018 · 1 min · jiezi

把GitHub作为图床

背景最近又迷恋上了写博客,尤其是前一段时间很想要写点东西分享一些软件的使用感想。但当写完文章想要发表时就会碰到一个问题:由于我是现在本机的编辑器中用Markdown写好了全文的内容,再发表到各个平台(曾经是GitHub Pages搭建的博客,后来又多了简书,现在再加上SegmentFault)上的,因此文章里的图片都是引用在本地磁盘上的文件路径的。这么一来,如果直接将文章源码粘贴到博客平台上——比如粘贴到SegmentFault中,那么这些本地的图片链接就无法在发布后的文章中正常显示了。如果一开始就在SegmentFault中写作也会遇到问题。SegmentFault上的文章插入图片后,并不是像普通的Markdown源码那般插入一条形式的标记的,而是像下图这样显然,这样的文章源码复制到其它平台(GitHub Pages、简书)去发布的话,必然是需要针对其中的图片标记修改一番的——比刚开始的方法或许要更麻烦。看来要解决这个图片链接在不同平台间共用的问题,必须有一处纯粹的用于存放图片文件的地方——也就是大家常说的图床了。刚开始我也放狗搜了一下,看看别人的推荐,印象中得到的答复不外乎是又○云、七○云、新○微博,以及sm.ms等。但它们要么需要注册并且实名认证,要么不纯粹,要么让人觉得随时会丢失。某个晚上忽然想到,GitHub不就是一个很好的图床么?!在GitHub上建一个仓库专门存放博客中的图片,不仅免费、完全受自己管理,而且自带CDN加速,并且我的读者群(如果真的有这么一个群体的话)也应当可以畅通地访问GitHub。放图片的仓库虽然有了,但用起来还不是很便利——因为作为写作素材的图片在我的电脑上是存放在一个单独的、非GitHub仓库的目录下的,所以如果要丢到图床上,就需要先将文件复制过去,然后执行git的add、commit、push三部曲,最后还要到GitHub上复制这张新图片的“raw”地址。这个过程很机械化,完全可以用一个Alfred的Workflow来代劳。编写Workflow编写Workflow就像编写Common Lisp中的宏一样,总是从它们的用法入手的。在我的设想中,这个Workflow的使用方式应当是:首先,按下快捷键调出Alfred的输入框,输入关键字(在我这里就叫做upload)来唤起这个Workflow;然后,输入要上传的图片文件的绝对路径并按下回车,开始在后台处理最后,上传完毕后,弹出通知来告诉我整个Workflow的概貌其实很简单第二个节点所调用的External Script是长这样子的#!/bin/bash# 将磁盘文件上传到GitHubpath=${1}pictures_dir="${HOME}/Documents/Projects/riverbed/pictures"cp “${path}” “${pictures_dir}“echo ‘文件复制完毕’file=$(basename “${path}")cd “${pictures_dir}“git add “${file}“git commit -m ‘上传一张图片’git push -u origin masterecho ‘文件已提交到GitHub’/usr/local/bin/node -e “console.log(encodeURI(‘https://raw.githubusercontent.com/Liutos/riverbed/master/pictures/${file}'));" | tr -d ‘\n’ | pbcopy获取文件的绝对路径其实很简单,在Finder中选中文件后,按下Command+Option+C即可这里使用basename命令获取文件名。并且,为了避免git打开文本编辑器要求输入commit message,向git-commit命令传递了-m选项。因为文件名含有非ASCII的字符(毕竟会有中文),需要做一次URL编码,因此用了node来做转换。在Node.js代码中用console.log输出编码后的图片URL,结尾会有一个换行符,所以用tr将其去掉。最后,输出的内容重定向给pbcopy,就将上传后的图片URL复制到剪贴板中了。如果此时正在编辑文章,便可以粘贴这个图片的链接到源码中。Alfred也提供Copy to Clipboard,用于将Workflow中上一个节点的输出复制到剪贴板中。之所以不使用,其实是因为刚开始的时候就是用的Alfred的Copy to Clipboard,结果发现git运行过程中的输出也被Alfred接收了,跟图片URL一起混进了剪贴板中。所以最后改为直接调用pbcopy。全文完。

December 7, 2018 · 1 min · jiezi

开源博客|Theme-bmw:版本微声发布

Theme-BMW是一款基于HEXO开发的博客框架:提供了友链、关于、标签云等页面,支持数学公式渲染、文章目录、多级导航栏,自带评论系统和浏览统计插件,并且增加了文章分享、打赏、版权声明音乐播放等功能。这次的版本代号是“微声”:旨在号召每个人为自己发声!源码地址???????? 中文文档:https://godbmw.com/passages/2018-11-15-theme-bmw-docs-zh/???? Star On Github:https://github.com/dongyuanxin/theme-bmw在线演示???? 首页页面: https://godbmw.com/????️ 归档页面: https://godbmw.com/archives/???? 分类页面: https://godbmw.com/categories/????️ 标签页面: https://godbmw.com/tags/???? 友链页面: https://godbmw.com/friends/????️ 关于页面: https://godbmw.com/about/特性一览写文章 · 随心而动支持目录渲染/关闭支持多平台分享支持用户打赏支持图片幻灯片播放支持数学公式渲染支持您为不同文章自定义的音乐播放器多页面 · 一键启动支持二级导航栏自带友链页面自带关于页面自带标签云页面自带分类云页面支持自定义页面社交 · 不止你我自带评论系统自带文章统计插件版权 · 保护原创支持文章版权声明和相关信息自定义支持自定义页脚个人信息自定义 · 个性彰显支持自定义样式文件支持自定义脚本文件其它 · SEO | 极速 | …联系方式???? Email: yuanxin.me@gmail.com???? QQ: 2181111110???? 交流群:

December 6, 2018 · 1 min · jiezi

2019怎么样打造自己的“前端品牌”

这个年底相信对于很多程序猿来说都不算太好过,是的,资本的寒冬已经到来。无论是传言某厂停止社招还是某商城末尾淘汰,亦或者某知名论坛因为“现金流”问题大裁员。这个年底,已经听到了很多知名公司裁员的消息了。关于裁员的思考。首先,资本总是逐利的,当然这是句废话,公司又不是散财童子或者观世音菩萨,公司招人肯定是要赚钱的。再者是互联网行业经历了一个巨大的风口,站在风口上,母猪也能上天。are you ok?互联网行业经历了一段热钱岁月,也催生了一堆泡沫。裁员呢,从公司的角度来看,为了利益的最大化,这两类人是比较危险的。高 p 人员。他们是公司的技术骨干,拿着丰厚的薪水以及股份。对于有些公司而言,高 p 人员消耗了大量的资源。能力靠后的人。这里的能力不只是说技术能力,包括各方面的能力。对于公司而言,这类人的单位产出比太低,甚至可能是负产出。总结就是公司裁员会留下那些要钱少,能做事的人。(老板不包括里面啊)朋友面试的困惑在微信群里经常有人问我,没有项目经验要怎么办,没有出彩的简历该怎么弄?等你看完这篇文章,如果能给你一些帮助的话,那将是我的荣幸。为什么要打造自己的“前端品牌”从面试官的角度来看,面试官只能从你的简历中找出你的亮点,来初步判断你是否适合这个岗位。那么作为前端的面试官,候选者的哪些品质会吸引到面试官呢?开源项目经历。(一般指 github)对自己的项目比较了解有那么一两个很熟悉的技术,比如 canvas,node 都是加分项对常用的框架源码有一定的了解有自己的博客有一定的自驱力漂亮的程序媛有开源项目参与经历至少说明候选者是一个乐于分享,熟悉基本的 git 流程,如果是一些知名项目的话还能体现出候选者的能力水平。对自己项目了解在面试中也是非常重要的一环,因为面试官会根据你写的项目经历去详细问你看你对技术的掌控程度。加分项就不用说了,可以体现出候选者好学。框架源码是大厂必考的一个点之一,为什么呢?因为大厂一般都会使用自己开发的框架,现代框架特性是基本相似的,虚拟 dom,diff,状态管理,路由等,面试官希望候选者能够了解框架的底层原理,而不是 api 的搬运工。自驱力一般指的是候选者的态度,比如学习的毅力,推动团队迭代,分享等。这篇文章着重讲的是有自己的博客。怎么样打造自己的 “前端品牌”积极参与开源项目github 上有各种各样的开源项目,有些项目非常有趣。通过参与 github 上面的开源项目,你可以认识特别多有趣的人,同时也能给你的简历增加权重。比如你给 vue.js 提供过几个 pr 并且成为贡献者之一,或者给 antd 修复了几个 bug,或者参与了某某翻译计划。如果你热爱开源(或者为了面试),你甚至可以花式提 pr。为了开源社区更好的发展,给出下列花式混 pr 的方式开源项目之初会有很多的问题,比如文档不完善,翻译错误,代码 bug 等。正是混 pr 的好时候关注知名开源项目,往往 issues 里面会存在很多的问题,如果你可以帮忙修复一些问题并提交 pull request,是不是既帮助了别人也成就了自己了呢。上面的建议虽然不入流,但是也从侧面推动了开源社区的发展,你也可以混得知名项目的贡献者荣誉。且不问你是怎么获得的,至少你付出了,就比别人更进一步,这就是你的优势。积累日常学习的东西,形成文档。俗语道:好记性不如烂笔头。虽然是陈词滥调,对于程序猿(媛)而言也一般不用“笔”,但确实是一件有意义的事情。我的几任上司都鼓励我多进行分享,多写文档去沉淀自己的技术。正是由于他们分享的这种精神,让我深受感染,也将会一直影响我未来的道路。技术的分享确实是一件非常令人兴奋的事情,一方面来说,分享的时候,你可以听到不同的声音,一个人的力量是有限的,这将帮助你去拓宽你的视野。分享的时候,很多人也会对细节进行详细的询问,你在讲解回答的过程中可以进一步加深自己的理解。另一方面,分享能够提高一个人的专注度,你总是渴望去把最好的东西展示出来,无形之中,你就学到了很多的东西。有一个很现实的问题是,如果你在开发的过程中遇到了一个很困难的问题,你通过搜索,询问各种方式去解决了。但是你没有文档,很久之后,你又遇到了相同的问题,结果你忘了,是不是又要重复去做这些无聊的动作。作为一个管理者而言,假如你的一个下属遇到了一个问题解决了,后面这个人离职了,又来了另外一个人,又是同样的问题,不知所措。这个时候你是不是很崩溃?所以大厂一定会有自己的文档库,(一定会有)。面试官从简历里面能够了解到的,除了你的工作经历,最主要的还是看你的博客,开源项目。写博客是需要坚持的一件事情,毅力很重要。也许刚开始你的文章并不出色,随着时间的推移,技术能力的上升。会越来越精彩,从而打造出你自己的“博客品牌”。我从准备做“前端指南”公众号开始,已经连续 2 个半月早上 6 点准时更新了。只要你能坚持 21 天,后续的就会保持习惯。给初入门写博客的同学们一些建议。前端领域的知识点文章其实都差不多,大家来来回回看的都是差不多的。比如原型链,es6,源码解析,某某开源项目实践。虽然很俗,对于个人而言,如果自己能写一些,对于基础的掌控还是非常有帮助的。借木易杨的计划来看看有哪些文章是可以写的【进阶 1 期】 调用堆栈【进阶 2 期】 作用域闭包【进阶 3 期】 this 全面解析【进阶 4 期】 深浅拷贝原理【进阶 5 期】 原型 Prototype【进阶 6 期】 高阶函数【进阶 7 期】 事件机制【进阶 8 期】 Event Loop 原理【进阶 9 期】 Promise 原理【进阶 10 期】Async/Await 原理【进阶 11 期】防抖/节流原理【进阶 12 期】模块化详解【进阶 13 期】ES6 重难点【进阶 14 期】计算机网络概述【进阶 15 期】浏览器渲染原理【进阶 16 期】webpack 配置【进阶 17 期】webpack 原理【进阶 18 期】前端监控【进阶 19 期】跨域和安全【进阶 20 期】性能优化【进阶 21 期】VirtualDom 原理【进阶 22 期】Diff 算法【进阶 23 期】MVVM 双向绑定【进阶 24 期】Vuex 原理【进阶 25 期】Redux 原理【进阶 26 期】路由原理【进阶 27 期】VueRouter 源码解析【进阶 28 期】ReactRouter 源码解析这些基本上也是各个公司面试会考的一些内容,各位可以从这上面入手。也可以参考 冴羽 的博客进行写作。等到达到一定的水平后,可以去写一些前沿的文章,这对面试而言是很有用的。写在最后希望能够帮助到大家。帮助他人成长,让大家成长到和我一样的水平,这对于我而言也是一种帮助 –我老大 ...

December 6, 2018 · 1 min · jiezi