使用 Python 从零开始开发区块链应用程序
链客,专为开发者而生,有问必答!
此文章来自区块链技术社区,未经允许拒绝转载。
“区块链”是什么?
区块链是一种存储数字数据的方式。数据可以是任何内容。对于比特币,它是事务(在帐户之间转移比特币),它甚至可以是文件;这都无关紧要。数据是以区块形式进行存储的,区块使用哈希值链接在一起。因此得名“区块链”。
区块链的神奇之处是在其中添加和存储此类数据的方式,该方式造就了一些非常理想的特征:
历史记录无法更改
系统无法攻破
数据的持久保存
没有单点故障
那么区块链如何能够实现这些特征呢?我们将通过实现一个区块链来深入剖析它。让我们开始吧。
关于该应用程序
首先定义一下我们将要构建的应用程序的用途。我们的目的是构建一个允许用户共享信息的简单网站。因为内容将存储在区块链中,所以它无法更改且会永远存在。
我们将采用自下而上的实现方式。首先定义我们将存储在区块链中的数据的结构。 一篇帖子(任何用户在我们的应用程序上发布的一条消息)将由 3 个基本要素来标识:
1
将事务存储到区块中
我们将采用一种广泛使用的格式来将数据存储在区块链中:JSON。以下是一篇存储在区块链中的帖子的格式:
{
"author": "some_author_name","content": "Some thoughts that author wants to share","timestamp": "The time at which the content was created"
}
术语“数据”通常在互联网上被“事务”一词所取代。所以,为了避免混淆并保持一致,我们将使用术语“事务”来表示在我们的示例应用程序中发布的数据。
事务被打包到区块中。一个区块可以包含一个或许多个事务。包含事务的区块频繁地生成并添加到区块链中。因为可能有多个区块,所以每个区块都应有一个唯一 ID:
class Block:
def __init__(self, index, transactions, timestamp):self.index = []self.transactions = transactionsself.timestamp = timestamp
2
让区块不可更改
我们希望检测出对区块内存储的数据的任何篡改。在区块链中,这是使用一个哈希函数来实现的。
哈希函数接受任何大小的数据并生成固定大小的数据,该结果通常用于识别输入。下面是 Python 中的一个使用 sha256 哈希函数的示例:
from hashlib import sha256
data = “Some variable length data”
sha256(data).hexdigest()
‘b919fbbcae38e2bdaebb6c04ed4098e5c70563d2dc51e085f784c058ff208516’
sha256(data).hexdigest() # no matter how many times you run it, the
result is going to be the same 256 character string
‘b919fbbcae38e2bdaebb6c04ed4098e5c70563d2dc51e085f784c058ff208516’
一个理想的哈希函数包括以下特征:
它应该很容易计算。
哪怕只更改数据中的一个位,哈希值也应该完全发生变化。
应该无法根据输出哈希值猜出输入。
您现在知道哈希函数是什么了吧。我们将每个区块的哈希值都存储在 Block 对象内的一个字段中,其作用类似于它所包含的数据的数字指纹:
from hashlib import sha256
import json
def compute_hash(block):
"""A function that creates the hash of the block."""block_string = json.dumps(self.__dict__, sort_keys=True)return sha256(block_string.encode()).hexdigest()
备注:在大多数加密货币中,甚至对区块中的各个事务也进行了哈希运算,从而形成一棵哈希树(也称为二进制哈希树),这棵树的根可以用作区块的哈希值。它不是区块链正常运作的必要条件,所以我们将省略它,以保持代码简洁。
3
链接区块
我们已设置了区块。区块链应该是一个区块集合。我们可以将所有区块都存储在 Python 列表中(等效于数组)。但这还不够,因为如果有人故意替换了集合中的一个区块该怎么办?用修改过的事务创建一个新的区块,计算哈希值,然后替换任何旧区块,这在我们的当前实现中并不是什么难事,因为我们会保持区块的不可更改性和顺序。
我们需要采用某种途径来确保对过去的区块的任何更改都会造成整个链的失效。一种方法是通过哈希值将区块链接起来。谈到链接,我们指的是将前一个区块的哈希值包含在当前区块中。所以,如果任何以前的区块的内容发生更改,该区块的哈希值也会发生更改,导致与下一个区块中的 previous_hash 字段不匹配。
每个区块都通过 previous_hash 字段链接到前一个区块,但是第一个区块该如何处理?第一个区块称为创始区块,大多数情况下,它是手动生成或通过某种独特逻辑生成的。让我们将 previous_hash 字段添加到 Block 类中,并实现我们的 Blockchain 类的初始结构(参见清单 1)。
清单 1. 我们的 Blockchain 类的初始结构
from hashlib import sha256
import json
import time
class Block:
def__init__(self, index, transactions, timestamp, previous_hash): self.index = indexself.transactions = transactionsself.timestamp = timestampself.previous_hash = previous_hashdef compute_hash(self): block_string = json.dumps(self.__dict__, sort_keys=True)return sha256(block_string.encode()).hexdigest()
这是我们的 Blockchain 类:
class Blockchain:
def __init__(self):self.unconfirmed_transactions = [] # data yet to get into blockchainself.chain = []self.create_genesis_block()def create_genesis_block(self):"""A function to generate genesis block and appends it tothe chain.The block has index 0, previous_hash as 0, anda valid hash."""genesis_block = Block(0, [], time.time(), "0")genesis_block.hash = genesis_block.compute_hash()self.chain.append(genesis_block)@propertydef last_block(self):return self.chain[-1]
4
实现工作量证明算法
选择性背书与工作量证明
IBM Blockchain Platform 支持的业务区块链中的共识性不是通过挖矿实现的,而是通过一个称为选择性背书的流程来实现的。网络成员准确控制由谁来验证事务,与目前的业务实现方式大致相同。进一步了解业务区块链。
但这里存在一个问题。如果我们更改前一个区块,我们可以非常轻松地重新计算所有后续区块的哈希值,并创建一个不同的有效区块链。为了预防这种情况,我们必须让计算哈希值的任务变得困难和随机化。
以下是我们实现此操作的方式。我们不会接受任何区块哈希值,而是对它添加某种约束。让我们来添加一种约束:哈希值应以两个前导零开始。另外我们知道,除非更改区块的内容,否则哈希值不会发生更改。
所以我们将在区块中引入一个称为随机数的新字段。随机数会不断变化,直到我们获得满足约束条件的哈希值。前导零的数量(在我们的例子中为值 2)决定了工作量证明算法的“难度”。您可能还注意到,我们的工作量证明很难计算,但在我们确定随机数后很容易验证(对于验证,您只需要再次运行哈希函数即可):
class Blockchain:
# difficulty of PoW algorithmdifficulty = 2"""Previous code contd.."""def proof_of_work(self, block):"""Function that tries different values of nonce to get a hashthat satisfies our difficulty criteria."""block.nonce = 0computed_hash = block.compute_hash()while not computed_hash.startswith('0' * Blockchain.difficulty):block.nonce += 1computed_hash = block.compute_hash()return computed_hash
请注意,没有明确的逻辑来快速确定随机数;只能通过暴力破解。
5
将区块添加到链中
要将区块添加到链中,首先需要验证所提供的工作量证明是否正确,以及要添加的区块的 previous_hash 字段是否指向链中的最新区块的哈希值。
让我们看看将区块添加到链中的代码:
class Blockchain:
"""Previous code contd.."""def add_block(self, block, proof):"""A function that adds the block to the chain after verification."""previous_hash = self.last_block.hashif previous_hash != block.previous_hash:return Falseif not self.is_valid_proof(block, proof):return Falseblock.hash = proofself.chain.append(block)return Truedef is_valid_proof(self, block, block_hash):"""Check if block_hash is valid hash of block and satisfiesthe difficulty criteria."""return (block_hash.startswith('0' * Blockchain.difficulty) andblock_hash == block.compute_hash())
挖矿
事务最初存储在一个未确认事务池中。将未确认事务放入区块中并计算工作量证明的过程被称为区块挖矿。一旦找到满足我们的约束条件的随机数,我们就可以说挖到了一个区块,这个区块就会放入区块链中。
在大多数加密货币(包括比特币)中,作为对耗费算力来计算工作量证明的奖励,矿工可以获得一些加密货币。以下是我们的挖矿函数的格式:
class Blockchain:
"""Previous code contd..."""def add_new_transaction(self, transaction):self.unconfirmed_transactions.append(transaction)def mine(self):"""This function serves as an interface to add the pendingtransactions to the blockchain by adding them to the blockand figuring out Proof of Work."""if not self.unconfirmed_transactions:return Falselast_block = self.last_blocknew_block = Block(index=last_block.index + 1,transactions=self.unconfirmed_transactions,timestamp=time.time(),previous_hash=last_block.hash)proof = self.proof_of_work(new_block)self.add_block(new_block, proof)self.unconfirmed_transactions = []return new_block.index
好了,我们的工作差不多完成了。您可以在 GitHub 上查看截至目前的合并代码。
6
创建接口
现在为我们的节点创建接口,以便与其他对等节点以及我们将要构建的应用程序进行交互。我们将使用 Flask 创建一个 REST-API 来与我们的节点进行交互。以下是它的代码:
from flask import Flask, request
import requests
app = Flask(name)
the node’s copy of blockchain
blockchain = Blockchain()
我们的应用程序需要一个端点来提交新事务。我们的应用程序将使用此端点将新数据(帖子)添加到区块链中:
@app.route(’/new_transaction’, methods=[‘POST’])
def new_transaction():
tx_data = request.get_json()required_fields = ["author", "content"]for field in required_fields:if not tx_data.get(field):return "Invlaid transaction data", 404tx_data["timestamp"] = time.time()blockchain.add_new_transaction(tx_data)return "Success", 201
下面是返回节点的链副本的端点。我们的应用程序将使用此端点来查询要显示的所有帖子:
@app.route(’/chain’, methods=[‘GET’])
def get_chain():
chain_data = []for block in blockchain.chain:chain_data.append(block.__dict__)return json.dumps({"length": len(chain_data),"chain": chain_data})
下面是请求节点挖掘未确认事务(如果有)的端点。我们将使用此端点从我们的应用程序自身发起一个挖矿命令:
@app.route(’/mine’, methods=[‘GET’])
def mine_unconfirmed_transactions():
result = blockchain.mine()if not result:return "No transactions to mine"return "Block #{} is mined.".format(result)
endpoint to query unconfirmed transactions
@app.route(’/pending_tx’)
def get_pending_tx():
return json.dumps(blockchain.unconfirmed_transactions)
app.run(debug=True, port=8000)
现在,您可以体验一下我们的区块链,创建一些事务,然后使用诸如 cURL 或 Postman 之类的工具来挖掘它们。
7
建立共识和去中心化
目前为止,我们实现的代码只能在单个计算机上运行。即使通过哈希值链接了区块,我们仍然不能信任单个实体。我们需要多个节点来维护我们的区块链。 所以让我们创建一个端点,以便让一个节点了解网络中的其他对等节点:
the address to other participating members of the network
peers = set()
endpoint to add new peers to the network.
@app.route(’/add_nodes’, methods=[‘POST’])
def register_new_peers():
nodes = request.get_json()if not nodes:return "Invalid data", 400for node in nodes:peers.add(node)return "Success", 201
您可能已经认识到,在多节点方面存在一个问题。由于故意操纵或意外的原因,一些节点的链副本可能有所不同。在这种情况下,我们需要商定采用链的某个版本,以维持整个系统的完整性。我们需要达成共识。
一种简单的共识算法可能是,在网络中的不同参与者构成的链出现分歧时,商定采用最长的有效链。选择此方法的理由是,最长的链是对已完成的最多工作量的有效估算:
def consensus():
"""Our simple consensus algorithm.如果找到一个更长的有效链,则用它替换我们的链。"""global blockchainlongest_chain = Nonecurrent_len = len(blockchain)for node in peers:response = requests.get('http://{}/chain'.format(node))length = response.json()['length']chain = response.json()['chain']if length > current_len and blockchain.check_chain_validity(chain):current_len = lengthlongest_chain = chainif longest_chain:blockchain = longest_chainreturn Truereturn False
最后,我们需要开发一种方法,让任何节点向网络宣布它已经挖到一个区块,以便每个人都能更新他们的区块链,并继续挖掘其他事务。其他节点可以轻松地验证工作量证明,并将它添加到各自的链中:
endpoint to add a block mined by someone else to the node’s chain.
@app.route(’/add_block’, methods=[‘POST’])
def validate_and_add_block():
block_data = request.get_json()block = Block(block_data["index"], block_data["transactions"],block_data["timestamp", block_data["previous_hash"]])proof = block_data['hash']added = blockchain.add_block(block, proof)if not added:return "The block was discarded by the node", 400return "Block added to the chain", 201def announce_new_block(block):for peer in peers:url = "http://{}/add_block".format(peer)requests.post(url, data=json.dumps(block.__dict__, sort_keys=True))
announce_new_block 方法应在节点挖到每个区块后调用,以便对等节点能将该区块添加到自己的链中。
8
构建应用程序
好了,后端都设置好了。您可以在 GitHub 上查看到目前为止的代码。
现在,是时候创建应用程序的接口了。我们使用了 Jinja2 模板来呈现网页和一些 CSS,让页面看起来美观一些。
我们的应用程序需要连接到区块链网络中的某个节点,以便抓取数据和提交新数据。也可能存在多个节点:
import datetime
import json
import requests
from flask import render_template, redirect, request
from app import app
.
CONNECTED_NODE_ADDRESS = “http://127.0.0.1:8000”
posts = []
fetch_posts 函数从节点的 /chain 端点获取数据,解析该数据并将它们存储在本地。
def fetch_posts():
get_chain_address = "{}/chain".format(CONNECTED_NODE_ADDRESS)response = requests.get(get_chain_address)if response.status_code == 200:content = []chain = json.loads(response.content)for block in chain["chain"]:for tx in block["transactions"]:tx["index"] = block["index"]tx["hash"] = block["previous_hash"]content.append(tx)global postsposts = sorted(content, key=lambda k: k['timestamp'],reverse=True)
该应用程序有一个 HTML 表单,用于接受用户输入,然后向一个已连接的节点发出一个 POST 请求,以便将该事务添加到未确认事务池中。然后,通过网络对该事务进行挖掘,最后在我们刷新网站后抓取该事务:
@app.route(’/submit’, methods=[‘POST’])
def submit_textarea():
"""Endpoint to create a new transaction via our application."""post_content = request.form["content"]author = request.form["author"]post_object = {'author': author,'content': post_content,}# Submit a transactionnew_tx_address = "{}/new_transaction".format(CONNECTED_NODE_ADDRESS)requests.post(new_tx_address,json=post_object,headers={'Content-type': 'application/json'})return redirect('/')
9
运行应用程序
大功告成!可以在 GitHub 上找到最终代码。
要运行该应用程序:
启动一个区块链节点服务器:
python node_server.py
运行该应用程序:
python run_app.py
您应该能在 http://localhost:5000 上看到正在运行的应用程序。
尝试发布一些数据,您会看到类似下图的结果:
单击 Request to mine 按钮,您会看到类似下图的结果:
单击 Resync 按钮,您会看到应用程序与链重新同步:
验证事务
您可能注意到了应用程序中的一个缺陷:任何人都能更改任何名称和发布任何内容。解决此问题的一种方法是,使用公私密钥加密来创建帐户。每个新用户都需要一个公钥(类似于用户名)和私钥,才能在我们的应用程序中发布内容。这些密钥可以充当数字签名。公钥只能对使用相应私钥加密的内容进行解密。在将事务添加到任何区块之前,会使用作者的公钥对其进行验证。这样,我们就知道是谁写的这条消息。
结束语
本教程介绍了公有区块链的基础知识。如果您一直在跟随操作,那么您已经从零开始实现了一个区块链,并构建了一个简单应用程序来允许用户在该区块链上共享信息。
后续行动
您可以在云上创建多个节点,并完善您构建的应用程序。您可以将任何 Flask 应用程序部署到 IBM Cloud。
此外,您还可以使用诸如 ngrok 之类的隧道服务为您的 localhost 服务器创建一个公有 URL,然后您就能与多台机器进行交互。
这一领域还有许多值得探索的地方!您可以通过以下方式继续增强您的区块链技能:
亲自体验新的 IBM Blockchain Platform Starter Plan(免费测试版),继续探索区块链技术。您可以快速建立一个区块链预生产网络,部署样本应用程序,开发和部署客户端应用程序。入门!
访问 developerWorks 上的区块链开发人员中心。可以在这里获得开发和部署业务区块链解决方案的工具和教程,以及代码和社区支持。
学习面向开发人员的区块链基础课程,了解资产转移的复杂细节。学完这门持续 2 小时的免费自学课程后,参加测验,获取一枚徽章,并开始为您的业务网络规划有用的区块链应用程序。
继续学习 IBM Blockchain 开发人员基础课程,这门持续 6 小时的免费课程在“区块链基础知识”的基础上进行了扩展,更详细地介绍了区块链业务网络的组件和架构,以及构建网络和创建应用程序的经验。
查阅许多区块链 Code Pattern,它们提供了解决复杂问题的路线图,包括概述、架构图、流程、存储库指南和其他阅读材料。
相关文章:

Mybatis学习记录-使用问题总结之一DISTINCT
问题1:手动修改的查询语句,放入到项目中后显示结果和实际查询结果不一致 由于实际情况中用的了分页功能,导致最终的语句在查询完成后,添加了分页项,即如下代码。 ROW_NUMBER() OVER ( ORDER BY COLUMNS) PAGE_ROW_NUMB…

python xlrd读取excel所有数据_python读取excel进行遍历/xlrd模块操作
我就废话不多说了,大家还是直接看代码吧~ #!/usr/bin/env python # -*- coding: utf-8 -*- import csv import xlrd import xlwt def handler_excel(filenamer/Users/zongyang.yu/horizon/ops_platform/assets/upload/1.xlsl): # 打开文件 workbook xlrd.open_work…

【NOIP2015提高组Day1】 神奇的幻方
【问题描述】 幻方是一种很神奇的 N*N矩阵:它由数字1,2,3, … … ,N*N 构成,且每行、每列及两条对角线上的数字之和都相同。 当N为奇数时,我们可以通过以下方法构建一个幻方: 首先将1写在第一行的中间。 之后,按如下…

40行python开发一个区块链
链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载。 尽管有人认为区块链目前还是个不成熟的解决方案,但它无疑称得上是计算机发展历史上的一个奇迹。但是,到底区块链是什么呢?…

网络实验的背景流
在最近做的网络实验中,发现背景流必须要先于实验流开始,并且要长于实验流的时间,这样才能看出实验流的规律。如果背景流后发于实验流,就会变成竞争模式,实验流就会被抢占或者挤压。转载于:https://www.cnblogs.com/fen…

python捕获异常后处理_python异常捕获处理
一、异常处理 在程序运行过程中,总会遇到各种各样的错误。程序一旦出错就停止运行了,此时就需要捕捉异常,通过捕捉到的异常,我们再去做对应的处理 写一个函数,实现除法运算 def calc(a,b): return a/b print(calc(5,1)…

《JS权威指南学习总结--第十一章子集和扩展》
js子集和扩展:http://www.cnblogs.com/ahthw/p/4298449.html ES6新增let和const关键字:http://www.cnblogs.com/telnetzhang/p/5639949.html JS中 var 和 let 关键字的区别:http://www.w3cfuns.com/notes/21400/891cac0f6bff2d7f25d3084618e8…

最常见的 35 个 Python 面试题及答案
链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载。 作为一个 Python 新手,你必须熟悉基础知识。在本文中我们将讨论一些 Python 面试的基础问题和高级问题以及答案,以帮助你完…

PHP 中日期时间函数 date() 用法总结
[导读] date()是我们常用的一个日期时间函数,下面我来总结一下关于date()函数的各种形式的用法,有需要学习的朋友可参考。格式化日期date() 函数的第一个参数规定了如何格式化日期 时间。它使用字母来表示日期和时间 格式化日期date() 函数的第一个参数规…

mac mysql的安装
mac是重装的系统,很干净,没有xmpp等组合的服务器。 1. 安装mysql server https://dev.mysql.com/downloads/mysql/ 这里是官网地址,选择需要的版本下载,我下载的是第一个dmg的,进入后,会让登陆或注册&#…

windows系统和linux系统可以使用相同的js代码吗_「React 手册 」在 Windows 下使用 React , 你需要注意这些问题...
大家好,本篇内容,我要和大家聊聊使用 Windows 开发 React ,你需要注意的一些问题。首先说明下,我不是使用 windows 进行开发,因为其配置开发环境来说不是特别方便,我更喜欢 苹果mac 或者乌班图这样的系统&a…

以太坊:比特币 + 无限可能
链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载。 还记你得刚学编程时,第一次使用“对象”的感觉吗?还记你第一次尝试函数式编程的样子吗?这些编程范式࿰…

thinkphp5内置标签
thinkphp5内置标签 知道内置标签怎么用,查手册的时候好查 却功能的时候在里面找着来用 内置标签一览 1 内置标签2 3 变量输出使用普通标签就足够了,但是要完成其他的控制、循环和判断功能,就需要借助模板引擎的标签库4 功能了,系统…

python可视化窗口制作一个摇骰子游戏_使用python制作一个抽奖小游戏——骰子游戏...
1.模拟真实环境掷骰子 从Python标准库中调用模块:random——random中包含以各种方式生成随机数的函数 从random中引用randint这一函数——骰子都是有固定面数 from random import randint **2. **创建Die类**** 骰子属性sides(面数)默认为6面…

C#拾遗(一、基本类型)
1. C#是一种块结构语言,用花括号{}分块,但是用#region和#endregion来定义可以展开和折叠的代码区域 #region 这是引用区 using System; ...... #endregion 2. C#简单类型都是小写,bool,string类型要区别于Java的写法;float、decim…

我不喜欢Go语言的十个理由
链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载。 Go 语言有多火爆?国外如 Google、AWS、Cloudflare、CoreOS 等,国内如七牛、阿里、知乎等都已经开始大规模使用 Go 语言开发…

写扩展性好的代码:函数
http://blog.jobbole.com/107442/转载于:https://www.cnblogs.com/answercard/p/8862006.html

PHP学习笔记:万能随机字符串生成函数(已经封装好)
做验证码用到的,然后就把这个函数封装起来,使用时候要设置2个参数: $str设置里要被采集的字符串,比如: $strefasfgzsrhftjxjxjhsrth; 则在函数里面生成的字符串就回从efasfgzsrhftjxjxjhsrth里面随机抓取; …

python中getopt函数_python getopt模块详解
getopt这个函数 就是用来抽取 sys.argv 获得的用户输入来确定执行步骤。 getopt是个模块,而这个模块里面又有getopt 函数,所以getopt需要这样这样用。 getopt.getopt( [命令行参数列表], "短选项", [长选项列表] ) 该函数返回两个值. opts 和a…

Go语言的前景分析
链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载。 抓住时代的趋势 在上篇文章中,也就是那个跨维度的打击,是可以直接秒杀的中,提到这个时代的问题,这次…

SQL获取当月天数的几种方法
SQL获取当月天数的几种方法 原文:SQL获取当月天数的几种方法 日期直接减去int类型的数字 等于 DATEADD(DAY,- 数字,日期) 下面三种方法: 1,日期加一个月减去当前天数,相当于这个月最后一天的日期。然后获取天数。(注意,…

移动端zepot媒体查询media queries
使用zepot做轮播图<head> <meta charset"utf-8"> <meta name"viewport" content"widthdevice-width, user-scalableno, initial-scale1.0, maximum-scale1.0, minimum-scale1.0"> <title>jd首页</title> <link…

前端try catch是如何捕获异常的_一文告诉你如何优雅处理前端异常?
前端一直是距离用户最近的一层,随着产品的日益完善,我们会更加注重用户体验,而前端异常却如鲠在喉,甚是烦人。一、为什么要处理异常?异常是不可控的,会影响最终的呈现结果,但是我们有充分的理由…

区块链热度不断,那么究竟是泡沫还是未来?
链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载。 区块链究竟是泡沫还是未来,需要落脚到实际运用中去判断。区块链区别于传统服务器,其宗旨不是为现实世界带来某个特定的产品&…

集成 Kendo UI for Angular 2 控件
伴随着 Angular 2 的正式 release,Kendo UI for Angular 2 的第一批控件已经发布了,当前是 Beta 版本,免费使用。 官方站点:Kendo UI for Angular 2 Kendo UI for Angular 被打包成独立的多个 NPM package,在 Progres…

按钮垂直居中_带下拉按钮的动态图表
小伙伴们好啊,今天和大家一起分享一个图表制作的技巧,先来看看效果:这个图表里,其实有三个数据系列,分别是一深一浅两个颜色的条形图,再就是大大的圆圈儿,其实是用散点图模拟出来的。先来看数据…

第一周Access课总结
第一周Access课总结 1:这节课学到了什么? 这节课重点学了数据库是用来干什么 做什么的 老师怕我们理解不了 用了很长时间向我们举了很多的例子 让我们终于知道了数据库是用来干嘛的了 顾名思义 数据库就是存放数据的仓库 是长期存放在计算机内 有组织…

以太坊,EOS和其他DApps的总数达到2,432,但没有大规模采用
链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载。 根据分散应用监测网站StateOfTheDApps,每月创建的新DApps数量的最高水平是2018年12月。去年最后一个月共有179个新的DApps上线。 以太…

docker logstash_用于监视Kubernetes和Docker的六大开源工具
Kubernetes和Docker是在DevOps圈中最常听到的两个词。Docker是一个工具,它使你能够以容器化的方式运行应用程序,Kubernetes是一个用于编排、管理容器的平台——如果你想使用Docker CLI去手动地管理数千个容器,这是不切实际的。然而࿰…

大道至简第一章读后感
当今社会,信息化飞速发展,软件的需求也越来越高,而《大道至简》给我们点透了编程的精义。 大道至简第一是讲的便是编程的精义,文章借愚公移山这个故事来阐释编程的基本思路:出现一个问题(惩山北之塞&#x…