hyperledger v1.0.5 区块链运维入门(一)
hyperledger v1.0.5 区块链运维入门
本文作者最近在找工作,有意向致电 13113668890
Mr. Neo Chen (陈景峯), netkiller, BG7NYT
中国广东省深圳市龙华新区民治街道溪山美地 518131 +86 13113668890 <netkiller@msn.com>
版权声明
转载请与作者联系,转载时请务必标明文章原始出处和作者信息及本声明。
http://www.netkiller.cnhttp://netkiller.github.iohttp://netkiller.sourceforge.net |
http://www.netkiller.cn |
http://netkiller.github.io |
http://netkiller.sourceforge.net |
微信订阅号 netkiller-ebook (微信扫描二维码)QQ:13721218 请注明“读者”QQ群:128659835 请注明“读者” |
微信订阅号 netkiller-ebook (微信扫描二维码) |
QQ:13721218 请注明“读者” |
QQ群:128659835 请注明“读者” |
||||
---|---|---|---|---|---|---|---|---|---|---|---|
http://www.netkiller.cn | |||||||||||
http://netkiller.github.io | |||||||||||
http://netkiller.sourceforge.net | |||||||||||
微信订阅号 netkiller-ebook (微信扫描二维码) | |||||||||||
QQ:13721218 请注明“读者” | |||||||||||
QQ群:128659835 请注明“读者” |
2018-02-08
摘要
你网上搜索hyperledger大部分文章是讲解开发环境的安装与配置,没有一篇关于怎样运维区块链的文章。当你配置好开发环境,写好合约,怎样落地呢?却很少文章提及。
要将区块链落地,我们必须依赖运维技术,这是IT基础设施,区块链应用将建立在这个基础设施之上,否则区块链就是浮云,悬在空中无法落地。
目录
- 1. 背景
- 2. 部署拓扑
- 2.1. 依赖关系
- 2.2. 准备物理机
- 3. 生成证书和创世区块
- 3.1. 创建配置文件
- 3.1.1. crypto-config.yaml
- 3.1.2. configtx.yaml
- 3.2. 启动 fabric-tools 容器
- 3.2.1. 启动 Docker 容器
- 3.2.2. 生成证书
- 3.2.3. 生成创世区块
- 3.2.4. 生成通道配置文件
- 3.2.5. generate anchor peer transaction
- 3.2.6. 清理 Docker 容器
- 3.1. 创建配置文件
- 4. CouchDB 节点
- 4.1. 安装 CouchDB
- 4.2. 启动 CouchDB
- 4.3. 备份与恢复 CouchDB
- 5. CA 节点安装
- 5.1. docker-compose-ca.yml
- 5.2. 启动 CA 节点
- 6. Orderer 节点安装
- 6.1. docker-compose-orderer.yml
- 6.2. 启动 Orderer 节点
- 7. Peer 节点安装
- 7.1. docker-compose-peer.yml
- 7.2. 启动 Peer 节点
- 7.3. 创建 Channel
- 8. Tools 节点安装
- 8.1.
- 9. 验收与测试
- 10. 总结
1. 背景
由于区块链是区中心化,与传统运维不同,所以之前你积累的经验,不一定适用于区块链。要想运维好区块链项目,就必须理解去中心化这个概念。
首先谈谈传统运维,总结为三个字“中心化”,当然有人反对并抛出“分布式”感念,传统运维的分布式仍然建立在中心化的基础之上。
我们来看看传统应用模式,决多数应用都可以概括为:
用户 -> WEB -> Application -> Cache -> Database
可以在这个体系下面做灵活变化,例如加入所有引擎、分布式文件系统,大数据等等应用,但都离不开这个模式。
区块链完全不同,如果举一个最接近的例子,我想可能与多数据中心远程异地灾备比较接近。
2. 部署拓扑
什么是区块链呢? 区块链实际上就是数据库,一个只能插入和查询的数据库,数据不能被修改和删除,并且这个数据库没有DBA管理员角色。这么一说你应该明白了把,实际上运维区块链就是在维护一个分布式数据库。
网上的绝大多数安装例子中,均采用 docker 部署方案,但无一例外的是,全部安装在一个物理机上。如果是生产环境,我们必须分开不是,首先要做的工作是化整为零,拆解应用,搞明白每个容器的功能和作用。然后我们将应用拆分,独立部署到物理节点上去。
+---------------------------------+ | SDK | +---------------------------------+ | golang | nodejs | python | java | +---------------------------------+ | V +------------------------------+ | fabric-ca | +------------------------------+ | | V V +-------------------+ +-------------------+ | Peer | | Peer | +-------------------+ +-------------------+ | | | | V | | V +-----------+ | | +------------+ | Orderer | | | | Orderer | +-----------+ | | +------------+ V V +-------------------+ | Couchdb | +-------------------+
接下来我们要做的工作是将上面拓扑图种的技术点分分击破。
由于 Hyperledger Fabric 是建立在 Docker 基础之上的。所以不建议你去除 Docker 转而使用传统的本地编译安装方式。我们仍然保持使用 Docker 在每个物理节点上,省去软件的编译和安装环节。
2.1. 依赖关系
需要注意的是于其他传统系统一样,Hyperledger Fabric 的启动也是有顺序的,这是因为他们之间存在着依赖关系。
2.2. 准备物理机
物理机
- ca 节点,域名:ca.example.com,端口:7054
- orderer 节点,域名 orderer.example.com,端口:7050
- peer 节点,域名:peer.example.com,端口:7051、7053
- couchdb 节点,域名 couchdb.example.com,端口:5984
- tools 节点,域名:tools.example.com
3. 生成证书和创世区块
这里我们需要几个命令(configtxgen configtxlator cryptogen),官方的安装方式:
curl -sSL https://goo.gl/byy2Qj | bash -s 1.0.5
无论如何我都安装不成功,可能是(https://goo.gl/byy2Qj)被天朝给墙了。不过我发现 fabric-tools 里面有这个工具。
提示
经过访问外国网站发现 https://goo.gl/byy2Qj 地址是 301 到下面地址:
https://raw.githubusercontent.com/hyperledger/fabric/v1.0.5/scripts/bootstrap.sh
[root@localhost ~]# mkdir netkiller
[root@localhost ~]# cd netkiller/
[root@localhost netkiller]# mkdir -p {chaincode,crypto-config,config,artifacts}
3.1. 创建配置文件
3.1.1. crypto-config.yaml
创建证书
OrdererOrgs:
- Name: Orderer
Domain: example.com
Specs:
- Hostname: orderer
PeerOrgs:
- Name: Org1
Domain: org1.example.com
Template:
Count: 1
Users:
Count: 1
3.1.2. configtx.yaml
---
Profiles:
OneOrgOrdererGenesis:
Orderer:
<<: *OrdererDefaults
Organizations:
- *OrdererOrg
Consortiums:
SampleConsortium:
Organizations:
- *Org1
OneOrgChannel:
Consortium: SampleConsortium
Application:
<<: *ApplicationDefaults
Organizations:
- *Org1
Organizations:
- &OrdererOrg
Name: OrdererOrg
ID: OrdererMSP
MSPDir: crypto-config/ordererOrganizations/example.com/msp
- &Org1
Name: Org1MSP
ID: Org1MSP
MSPDir: crypto-config/peerOrganizations/org1.example.com/msp
AnchorPeers:
- Host: peer0.org1.example.com
Port: 7051
Orderer: &OrdererDefaults
OrdererType: solo
Addresses:
- orderer.example.com:7050
BatchTimeout: 2s
BatchSize:
MaxMessageCount: 10
AbsoluteMaxBytes: 99 MB
PreferredMaxBytes: 512 KB
Kafka:
Brokers:
- 127.0.0.1:9092
Organizations:
Application: &ApplicationDefaults
Organizations:
3.2. 启动 fabric-tools 容器
创建文件 docker-compose-tools.yml
version: '2'
networks:
basic:
services:
tools:
container_name: tools
image: hyperledger/fabric-tools
tty: true
environment:
- GOPATH=/opt/gopath
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
- CORE_LOGGING_LEVEL=DEBUG
- CORE_PEER_ID=cli
- CORE_PEER_ADDRESS=peer0.org1.example.com:7051
- CORE_PEER_LOCALMSPID=Org1MSP
- CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
- CORE_CHAINCODE_KEEPALIVE=10
# working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
working_dir: /root/netkiller
command: /bin/bash
volumes:
- /var/run/:/host/var/run/
- ~/netkiller:/root/netkiller
- ./chaincode/:/opt/gopath/src/github.com/
- ./crypto:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
networks:
- basic
3.2.1. 启动 Docker 容器
启动 Docker 容器
[root@localhost netkiller]# docker-compose -f docker-compose-tools.yml up -d
Creating tools
进入容器
[root@localhost netkiller]# docker-compose -f docker-compose-tools.yml exec tools bash
root@88e9040d2d2a:/opt/gopath/src/github.com/hyperledger/fabric/peer#
3.2.2. 生成证书
命令
cryptogen generate --config=./crypto-config.yaml
演示
root@8f467a88de99:~/netkiller# cryptogen generate --config=./crypto-config.yaml
org1.example.com
root@8f467a88de99:~/netkiller# ls -1 crypto-config
ordererOrganizations
peerOrganizations
3.2.3. 生成创世区块
root@8f467a88de99:~/netkiller# export FABRIC_CFG_PATH=$PWD
root@8f467a88de99:~/netkiller# configtxgen -profile OneOrgOrdererGenesis -outputBlock ./config/genesis.block
2018-02-08 08:35:30.121 UTC [common/configtx/tool] main -> INFO 001 Loading configuration
2018-02-08 08:35:30.236 UTC [common/configtx/tool] doOutputBlock -> INFO 002 Generating genesis block
2018-02-08 08:35:30.238 UTC [common/configtx/tool] doOutputBlock -> INFO 003 Writing genesis block
3.2.4. 生成通道配置文件
命令
CHANNEL_NAME=mychannel
configtxgen -profile OneOrgChannel -outputCreateChannelTx ./config/channel.tx -channelID $CHANNEL_NAME
操作演示
root@8f467a88de99:~/netkiller# CHANNEL_NAME=mychannel
root@8f467a88de99:~/netkiller# configtxgen -profile OneOrgChannel -outputCreateChannelTx ./config/channel.tx -channelID $CHANNEL_NAME
2018-02-08 08:41:08.010 UTC [common/configtx/tool] main -> INFO 001 Loading configuration
2018-02-08 08:41:08.020 UTC [common/configtx/tool] doOutputChannelCreateTx -> INFO 002 Generating new channel configtx
2018-02-08 08:41:08.020 UTC [common/configtx/tool] doOutputChannelCreateTx -> INFO 003 Writing new channel tx
3.2.5. generate anchor peer transaction
命令
CHANNEL_NAME=mychannel
configtxgen -profile OneOrgChannel -outputAnchorPeersUpdate ./config/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
操作演示
root@8f467a88de99:~/netkiller# CHANNEL_NAME=mychannel
root@8f467a88de99:~/netkiller# configtxgen -profile OneOrgChannel -outputAnchorPeersUpdate ./config/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
2018-02-08 08:46:19.162 UTC [common/configtx/tool] main -> INFO 001 Loading configuration
2018-02-08 08:46:19.176 UTC [common/configtx/tool] doOutputAnchorPeersUpdate -> INFO 002 Generating anchor peer update
2018-02-08 08:46:19.177 UTC [common/configtx/tool] doOutputAnchorPeersUpdate -> INFO 003 Writing anchor peer update
3.2.6. 清理 Docker 容器
至此所需的证书与创世区块都已生产完毕,fabric-tools 容易完成了它的使命,你可以继续保留或者清理干净。
[root@localhost netkiller]# docker-compose -f docker-compose-tools.yml down
Stopping tools ... done
Removing tools ... done
Removing network netkiller_basic
清理 tools 容器
docker rm -f $(docker ps -qa)
4. CouchDB 节点
整个 Hyperledger Fabric 技术栈中只有这个 CouchDB 是个外来户,看到 CouchDB 我就非常兴奋,这是一个NoSQL数据库(它与MongoDB十分类似),所以CouchDB 100%可以独立运行,且最容易分离。
CouchDB 在这里有两个方案可以选择。
- 采用 Docker 运行 CouchDB的方案。
- 采用传统方式物理机上本地安装 CouchDB
理论两种方案对实际结果没有什么区别,只需提供IP地址,用户名与密码供其他节点访问即可。但实际我们看到 Hyperledger Fabric 使用的镜像是 hyperledger/fabric-couchdb 不清楚是否有修改过 CouchDB 数据库。
如果你对 Docker 比较熟悉就采用 Docker 方案。如果不熟悉就采用本地安装方式。总之选择一种你能Hold住(掌控)的方案,一旦出现故障,你能第一时间排查并处理。
4.1. 安装 CouchDB
下面是 Docker 方案
[root@localhost netkiller]# vim docker-compose-couchdb.yml
version: '3'
networks:
basic:
services:
couchdb:
container_name: couchdb
image: hyperledger/fabric-couchdb
# Populate the COUCHDB_USER and COUCHDB_PASSWORD to set an admin user and password
# for CouchDB. This will prevent CouchDB from operating in an "Admin Party" mode.
environment:
- COUCHDB_USER=admin
- COUCHDB_PASSWORD=passw0rd
ports:
- 172.16.0.17:5984:5984
networks:
- basic
4.2. 启动 CouchDB
启动 Docker 容器
docker-compose -f docker-compose-couchdb.yml up -d
访问CouchDB管理界面,http://172.16.0.17:5984/_utils/ 请使用上面设置的密码进入。若想进入到容器内部可以使用下面命令:
docker-compose -f docker-compose-couchdb.yml exec couchdb bash
至此 CouchDB 节点部署完毕。
4.3. 备份与恢复 CouchDB
既然是运维区块链,对于运维工作我们最关心的就是如何备份数据,在出现故障的时候恢复数据。
npm install --save couchdb-backup-restore
var cbr = require('couchdb-backup-restore');
var config = {credentials: 'http://localhost:5984'};
function done(err) {
if (err) {
return console.error(err);
}
console.log('all done!');
}
// backup
cbr.backup(config, done).pipe(fs.createWriteStream('./db-backup.tar.gz'))
// restore
fs.createReadStream('./db-backup.tar.gz').pipe(cbr.restore(config, done));
5. CA 节点安装
CA 节点需要我们之前生成 crypto-config
5.1. docker-compose-ca.yml
version: '3'
networks:
basic:
services:
ca.example.com:
image: hyperledger/fabric-ca
environment:
- FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server
- FABRIC_CA_SERVER_CA_NAME=ca.example.com
- FABRIC_CA_SERVER_CA_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem
- FABRIC_CA_SERVER_CA_KEYFILE=/etc/hyperledger/fabric-ca-server-config/4239aa0dcd76daeeb8ba0cda701851d14504d31aad1b2ddddbac6a57365e497c_sk
ports:
- "XXX.XXX.XXX.XXX:7054:7054"
command: sh -c 'fabric-ca-server start -b admin:adminpw -d'
volumes:
- ./crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/fabric-ca-server-config
container_name: ca.example.com
networks:
- basic
5.2. 启动 CA 节点
docker-compose -f docker-compose-ca.yaml up -d
6. Orderer 节点安装
6.1. docker-compose-orderer.yml
version: '3'
networks:
basic:
services:
orderer.example.com:
container_name: orderer.example.com
image: hyperledger/fabric-orderer
environment:
- ORDERER_GENERAL_LOGLEVEL=debug
- ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
- ORDERER_GENERAL_GENESISMETHOD=file
- ORDERER_GENERAL_GENESISFILE=/etc/hyperledger/configtx/genesis.block
- ORDERER_GENERAL_LOCALMSPID=OrdererMSP
- ORDERER_GENERAL_LOCALMSPDIR=/etc/hyperledger/msp/orderer/msp
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/orderer
command: orderer
ports:
- 7050:7050
volumes:
- ./config/:/etc/hyperledger/configtx
- ./crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/:/etc/hyperledger/msp/orderer
- ./crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/:/etc/hyperledger/msp/peerOrg1
networks:
- basic
6.2. 启动 Orderer 节点
docker-compose -f docker-compose-orderer.yaml up -d
7. Peer 节点安装
7.1. docker-compose-peer.yml
version: '3'
networks:
basic:
services:
peer0.org1.example.com:
container_name: peer0.org1.example.com
image: hyperledger/fabric-peer
environment:
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
- CORE_PEER_ID=peer0.org1.example.com
- CORE_LOGGING_PEER=debug
- CORE_CHAINCODE_LOGGING_LEVEL=DEBUG
- CORE_PEER_LOCALMSPID=Org1MSP
- CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/peer/
- CORE_PEER_ADDRESS=peer0.org1.example.com:7051
# # the following setting starts chaincode containers on the same
# # bridge network as the peers
# # https://docs.docker.com/compose/networking/
- CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=${COMPOSE_PROJECT_NAME}_basic
- CORE_LEDGER_STATE_STATEDATABASE=CouchDB
- CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=172.16.0.17:5984
# The CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME and CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD
# provide the credentials for ledger to connect to CouchDB. The username and password must
# match the username and password set for the associated CouchDB.
- CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=admin
- CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=passw0rd
working_dir: /opt/gopath/src/github.com/hyperledger/fabric
command: peer node start
# command: peer node start --peer-chaincodedev=true
ports:
- 7051:7051
- 7053:7053
volumes:
- /var/run/:/host/var/run/
- ./crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp:/etc/hyperledger/msp/peer
- ./crypto-config/peerOrganizations/org1.example.com/users:/etc/hyperledger/msp/users
- ./config:/etc/hyperledger/configtx
#depends_on:
# - orderer.example.com
# - couchdb
networks:
- basic
Peer 需要连接到 CouchDB 注意配置项 CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=172.16.0.17:5984
同时连接CouchDB的用户与密码要正确
7.2. 启动 Peer 节点
[root@localhost netkiller]# docker-compose -f docker-compose-peer.yaml up -d
7.3. 创建 Channel
进入 Peer 容器
docker-compose -f docker-compose-peer.yaml exec peer0.org1.example.com bash
添加 Orderer 节点并创建 Channel
CORE_PEER_LOCALMSPID=Org1MSP
CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org1.example.com/msp
peer channel create -o orderer.example.com:7050 -c mychannel -f /etc/hyperledger/configtx/channel.tx
加入到 mychannel
CORE_PEER_LOCALMSPID=Org1MSP
CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org1.example.com/msp
peer channel join -b mychannel.block
查看通道
st t@f39764f58ff7:/opt/gopath/src/github.com/hyperledger/fabric# peer channel list
2018-02-09 08:12:46.454 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP
2018-02-09 08:12:46.454 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity
2018-02-09 08:12:46.456 UTC [channelCmd] InitCmdFactory -> INFO 003 Endorser and orderer connections initialized
2018-02-09 08:12:46.457 UTC [msp/identity] Sign -> DEBU 004 Sign: plaintext: 0A8A070A5C08031A0C08FEAFF5D30510...631A0D0A0B4765744368616E6E656C73
2018-02-09 08:12:46.458 UTC [msp/identity] Sign -> DEBU 005 Sign: digest: E27446498819AA4FE8EE835ADEF16195489975377A3C18D89C36D37AA24E5CA2
2018-02-09 08:12:46.469 UTC [channelCmd] list -> INFO 006 Channels peers has joined to:
2018-02-09 08:12:46.469 UTC [channelCmd] list -> INFO 007 mychannel
2018-02-09 08:12:46.469 UTC [main] main -> INFO 008 Exiting.....
8. Tools 节点安装
Tools 在生成创世区块的时候我们就曾经使用,你可以沿用之前的 tools 简单,或者创建一个 cli 节点,这个节点主要是用于管理区块链集群,例如合约部署,调试等等。
8.1.
version: '3'
networks:
basic:
services:
cli:
container_name: cli
image: hyperledger/fabric-tools
tty: true
environment:
- GOPATH=/opt/gopath
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
- CORE_LOGGING_LEVEL=DEBUG
- CORE_PEER_ID=cli
- CORE_PEER_ADDRESS=peer0.org1.example.com:7051
- CORE_PEER_LOCALMSPID=Org1MSP
- CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
- CORE_CHAINCODE_KEEPALIVE=10
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
command: /bin/bash
volumes:
- /var/run/:/host/var/run/
- ./chaincode/:/opt/gopath/src/github.com/
- ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
networks:
- basic
#depends_on:
# - orderer.example.com
# - peer0.org1.example.com
# - couchdb
- mysql主从同步(5)-同步延迟状态考量(seconds_behind_master和pt-heartbea)
- ngx_pagespeed-nginx前端优化模块介绍
- Tomcat利用MSM实现Session共享方案解说
- Tomcat集群环境下session共享方案梳理(1)-通过memcached(MSM)方法实现
- nginx负载均衡(5种方式)、rewrite重写规则及多server反代配置梳理
- python常用知识梳理
- 如何打击“假货、高仿”类小程序
- centos 6x系统下源码安装mysql操作记录
- 【3】利用Word模板生成文档的总结
- 【2】快速代码集的由来及概览
- silverlight数据绑定模式TwoWay,OneWay,OneTime的研究
- Silverlight数据绑定/IValueConverter学习笔记
- silverlight:DeepZoom版的图片局部放大效果
- Linq之ToDictionary<TSource, TKey, TElement>的写法
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法