标签归档:golang

golang标准库 —— strconv包

strconv包为我们提供的方法可以分校几类:

  • Append类
  • Format类
  • Parse类
  • Quote类
  • 其他

Append类

将各种类型转换为字符串后追加到 dst 尾部。

示例:

将各种类型转换为带引号字符串后追加到 dst 尾部。

示例:

Format类

Format类的方法主要是格式化对应类型的值,然后返回字符串

示例:

Parse类

这些类型的方法提供了转换类型的功能,一般都会返回第二个返回值错误类型

示例:

Quote类

示例:

其他

对基础方法的再封装

将整数转换为十进制字符串形式(即:FormatInt(i, 10) 的简写)

将字符串转换为十进制整数(即: ParseInt(s, 10, 0) 的简写)

示例

判断字符串是否可以不被修改的表示为一个单行的反引号字符串。
字符串中不能含有控制字符(除了 \t)和“反引号”字符,否则返回 false

示例:

判断 r 是否为可打印字符

示例:

转载参考:https://blog.csdn.net/wangshubo1989/article/details/75222002

golang标准库 —— errors包

error 接口

Go语言引入了一个关于错误处理的标准模式,即error接口,该接口定义:

对于大多数函数,如果要返回错误,大致可以定义为如下模式,将error作为多种返回值中的最后一个,但这并非是强制要求:

通过errors下的New方法可以创建一个错误:

error 源码分析

errors/errors.go下定义了errorString,并且让其实现了Error方法。

所以New方法创建的errorString是一个error类型。

实现自己的错误类型

golang标准库 —— container包

container包实现了三个复杂的数据结构:堆(heap),链表(list),环(ring)

链表-List

List结构体为链表本身,Element结构体是链表下的每一个元素。

Element

Element 结构体包含上一个元素和下一个元素的指针指向,所属List的指向,以及当前Element的值。

Element 提供了两个方法,获取上一个元素和下一个元素。

List

List本身包含了root Element主节点以及链表的长度。

创建一个List

创建List时,会进行初始化将root的上下节点都指向root本身。

回过头来我们会发现Element获取上下元素时会判断,下一个或者上一个元素是否root本身,如果是这回返回nil

List操作抽象

链表的操作我们都可以抽象为insertremove

insert,在指定节点插入某个节点。

  • 将上一个节点的next指向插入节点,将下一个节点的的prev指向插入节点
  • 将插入节点的next指向下一个节点,将插入节点的的prev指向上一个节点
  • 列表长度++, 插入节点指向list

remove, 操作起来就更简单了,直接将上下两个节点互指就可以了,然后放置内存泄漏清空删除节点的指向关系。

有了insertremove两个操作以后,我们可以扩展衍生到所有操作。在这里找几个比较典型的例子来说,

  • 移动至最前端:先将指定节点移除,然后将节点插入到root节点后

  • 移动至最后个节点:将节点移除,然后插入到root节点的上一个几点。

我们可以套用l.insert(l.remove(e), &Element{}),然后将节点移动到任意各节点去。

环-Ring

结构体Ring指的是环上的节点。相比于List它并没有元素与结构之分。Ring首位相连,形成一个环。

New

创建一个拥有10各节点的环。

Link & Unlink

Link将两个环相连,原理与链表的插入类似。

  • 将环A的尾与环B的头相连
  • 将环A的头与环B的尾相连

Unlink移除环中的某一段。

这里它的使用了一个技巧,先用Move方法获取第N+1个节点,然后将该节点传入Link方法,是第一个节点与第第N+1个节点相连,那么第2个节点到第N个节点就被删除了。

Do

循环ring操作。

堆-heap

heap下只有interface,并没有struct的实现,所以需要我们去实现。

heap.Interface嵌套了sort.Interface,所以我们一共需要实现5个方法,

我们自定义一个IntHeap,然后让其完成heap的一系列操作。

在定义好结构体,以及实现接口之后,我们对其进行操作,发现IntHeap自动实现了最小堆的功能。

为什么会这样呢?

主要因为heap中有两个函数up上浮和down下沉。

当我们操作heap的时候,会根据情况自动调用updown方法,完成最小堆的排序。

updown方法中,调用了我们实现的LessSwap方法取做相关操作。

参考:

  • https://books.studygolang.com/The-Golang-Standard-Library-by-Example/chapter03/03.3.html
  • https://ieevee.com/tech/2018/01/29/go-heap.html

搭建自己的以太坊区块链

一、基本概念

以太坊简单来说就是区块链与智能合约的结合,是基于solidity语言实现的。在以太坊中,智能合约也有一个帐户地址。

1.1、 EVM

  以太坊虚拟机(EVM)是以太坊中智能合约的运行环境。它不仅被沙箱封装起来,事实上它被完全隔离,运行在EVM内部的代码不能接触到网络、文件系统或者其它进程。甚至智能合约之间也只有有限的调用。

  以太坊支持两种智能合约的编程语言:Solidity 和 Serpent。Serpent 语言面临一些安全问题,现在已经不推荐使用了。Solidity 语法类似 JavaScript,它编译器 solc 可以把智能合约源码编译成以太坊虚拟机 EVM 可以执行的二进制码。
现在以太坊提供更方便的在线 IDE —— Remix https://remix.ethereum.org 使用 Remix,免去了安装 solc 和编译过程,它可以直接提供部署合约所需的二进制码和 ABI。

1.2、 Accounts

  以太坊中有两类账户,它们共用同一个地址空间。外部账户,该类账户被公钥-私钥对控制。合约账户,该类账户被存储在账户中的代码控制。 外部账户的地址是由公钥决定的,合约账户的地址是在创建合约时确定的

  每个账户都有一个以太币余额(单位是“Wei”),该账户余额可以通过向它发送带有以太币的交易来改变。

1.3、 Transactions

  每一笔交易都是一条信息,可以通过交易,将余额从一个帐户发至另一个帐户。

1.4、 Gas

  每一笔交易需要支付一定的gas。gas price是由创建者设置的,调用合约的发送账户需要交易费用 = gas price * gas amount。

1.5、 以太坊客户端

  以太坊客户端用于接入以太坊网络,进行账户管理、交易、挖矿、智能合约相关的操作。目前有多种语言实现的客户端,常用的有 Go 语言实现的 go-ethereum 客户端 Geth,支持接入以太坊网络并成为一个完整节点,也可作为一个 HTTP-RPC 服务器对外提供 JSON-RPC 接口。
  

二、搭建go-ethereum

两种安装方式:

  • ppa安装
  • 源码编译

2.1、ppa安装

2.2、源码编译

编译安装时 make geth 过程中可能会报错,可以通过下面方案进行修复

三、安装 Solidity 编译器

Solidity 编译器也有多种方法安装,参照 http://solidity.readthedocs.io/en/latest/installing-solidity.html 这里介绍最简单快捷的安装方式:PPA 直接安装。

3.1、ppa安装

四、搭建以太坊

4.1、创世区块

要运行以太坊私有链,需要定义自己的创世区块,创世区块信息写在一个 JSON 格式的配置文件中。首先将下面的内容保存到一个 JSON 文件中,例如 genesis.json

配置简单说明:

chainId指定了独立的区块链网络 ID。网络 ID 在连接到其他节点的时候会用到,以太坊公网的网络 ID 是 1,为了不与公有链网络冲突,运行私有链节点的时候要指定自己的网络 ID。不同 ID 网络的节点无法相互连接。

difficulty挖矿难度。

gasLimit区块 Gas 消耗限制。

4.2、初始化创世区块

目录结构

其中 geth/chaindata 中存放的是区块数据,keystore 中存放的是账户数据。

4.3、 启动以太坊区块链,并进入控制台模式

上面命令的主体是 geth console,表示启动节点并进入交互式控制台。
各选项含义如下:

  • datadir:指定区块链数据的存储位置;
  • identity:指定节点 ID
  • rpc:表示开启 HTTP-RPC 服务;
  • rpcport:指定 HTTP-RPC 服务监听端口号(默认为 8545);
  • port:指定和其他节点连接所用的端口号(默认为 30303);
  • nodiscover:关闭节点发现机制,防止加入有同样初始配置的陌生节点。

geth console进入一个交互式的 JavaScript 执行环境,在这里面可以执行 JavaScript 代码,其中 > 是命令提示符。在这个环境里也内置了一些用来操作以太坊的 JavaScript 对象,可以直接使用这些对象。这些对象主要包括:

  • eth:包含一些跟操作区块链相关的方法;
  • net:包含一些查看p2p网络状态的方法;
  • admin:包含一些与管理节点相关的方法;
  • miner:包含启动&停止挖矿的一些方法;
  • personal:主要包含一些管理账户的方法;
  • txpool:包含一些查看交易内存池的方法;
  • web3:包含了以上对象,还包含一些单位换算的方法。
4.3.1、创建账户

1)直接输入密码创建账户

2)交互式创建账户

4.3.2、查看账户余额

4.3.3、挖矿

开始挖矿

其中 start 的参数表示挖矿使用的线程数。第一次启动挖矿会先生成挖矿所需的 DAG 文件,这个过程有点慢,等进度达到 100% 后,就会开始挖矿,此时屏幕会被挖矿信息刷屏。

停止挖矿

4.3.4、交易

第一个账户已经有余额了了。第二个账户没有币。

账户解锁,只有先对账户解锁才能转账。

进行交易

此时如果没有挖矿,用 txpool.status 命令可以看到本地交易池中有一个待确认的交易,可以使用 eth.getBlock("pending", true).transactions 查看当前待确认交易。

进行挖矿交易。

新区块挖出后,挖矿结束,查看账户 1 的余额,已经收到了账户 0 的以太币

4.3.5、 查看交易与区块

1) 查看总区块数

2) 查看交易

3)通过区块号查看区块:

五、以太坊区块链网络

我们通过单机的不同端口来模拟不同的网络。

首先需要使用相同的创世区块来初始化一个链出来。

然后在chian2中创建一个账户

使用2个终端分别打开着2个链

目前启动了三个节点,但都默认关闭了发现功能,需要手动添加peer节点。

在每个终端输入admin.nodeInfo.enode, 把输出记录下来到static-nodes.json文件,我这里情况如下:

两个终端退出后重启。输入admin.peers可以查看节点信息

开始挖矿

chain2 链启动后会出现警告同步数据失败,等待的一段时间后就可以了。

增加节点还是使用admin.addPeer来处理。

addPeer() 的参数就是节点二的 enode 信息,注意要把 enode 中的 [::] 替换成节点二的 IP 地址。连接成功后,节点二就会开始同步节点一的区块,同步完成后,任意一个节点开始挖矿,另一个节点会自动同步区块,向任意一个节点发送交易,另一个节点也会收到该笔交易。

通过 admin.peers 可以查看连接到的其他节点信息,通过 net.peerCount 可以查看已连接到的节点数量。

除了上面的方法,也可以在启动节点的时候指定 --bootnodes 选项连接到其他节点。

参考链接:

  • https://mshk.top/2017/11/go-ethereum-1-7-2/
  • https://g2ex.github.io/2017/09/12/ethereum-guidance/
  • https://github.com/xiaoping378/blog/blob/master/posts/%E4%BB%A5%E5%A4%AA%E5%9D%8A-%E7%A7%81%E6%9C%89%E9%93%BE%E6%90%AD%E5%BB%BA%E5%88%9D%E6%AD%A5%E5%AE%9E%E8%B7%B5.md

gin框架入门

安装

快速入门

实现无缝重启、停机

GET, POST, PUT, PATCH, DELETE and OPTIONS

带参数

query 参数

post请求

上传文件

分组路由

  • 访问 192.168.31.105:8080/v1/submit 返回 v1.submit
  • 访问 192.168.31.105:8080/v2/submit 返回 v2.submit
使用中间件

请求日志记录文件

绑定参数

跳转

初识micro——安装入门

一、准备工作

开启代理(因为某些原因),下载源代码。

二、安装

安装micro,以及相关服务consulprotobuf

三、运行

3.1、安装案例

安装完成后,我们可以下载官方案例,然后进行运行查看是否安装成功。

3.2、启动相关服务

3.3、运行hello world

3.4、 其他方式

serve-client 模式

api 模式 (基于启动serve)

web模式(基于启动serve)

Golang 入门(七) —— 接口

Go语言的主要设计者之一罗布派克曾经说过,如果只能选择一个Go语言的特性移植到其他语言上去,他会选择接口。
Go语言中的接口有着至关重要的地位,如果说goroutinechannel是支撑起Go语言并发模型的基石,那么接口Go语言整个类型系统的基石。

Go语言中一个类只需要实现了接口需要的所有方法,那么我们可以说这个类实现了这个接口,例如:

共有4个接口,IFileIReadIWriteIClose

File类分别实现了以上4个接口所需要的方法,那么File同时实现了IFileIReadIWriteIClose这4个接口。

Go语言的接口是非侵入式的,看似制作了很小的语法调整,实则影响深远。

接口赋值

接口赋值分为两种情况:

  • 将对象实例赋值给接口
  • 将一个接口赋值给另一个接口

我们先讨论将对象实例赋值给接口,例如我们之前做过的Integer类型,如下:

相应的我们定义接口LessAdder如下,

现在有个问题,稼穑我们定义了一个Integer类型对象的实力,怎么将其复制给LessAdder接口呢?

以上应该使用表达式1呢还是使用表达式2呢?

正确答案应该是表达式2,为什么呢?

因为Go语言会根据下面的函数,

自动生成一个新的函数:

这样,类型*Integer就既存在Less方法,又存在Add方法,满足LessAdder接口。而另一方面来说,根据方法Add无法生成一下方法:

因此,类型Integer只存在Less方法,缺少Add方法,无法满足LessAdder接口,所以表达式1是不能被赋值的。

我们再来讨论另外一个赋值,将一个接口赋值给另一个接口

在Go语言中,只要两个接口拥有相同的方法列表,那么他们就是等同的,可以相互赋值。

在Go语言中,这两个接口并没有什么区别,因为:

  • 任何实现了IReadWrite的接口类,均实现了IStream
  • 任何IReadWrite接口对象可以赋值给IStream,反之亦然
  • 在任何地方使用IReadWrite接口与使用IStream并无差异

接口查询

我们可以把IStream的对象赋值给IWrite,但是反过来确认报错。

这个时候就需要用到接口的查询功能,

类型查询

在Go语言中,还可以更加直接了当的询问接口指向对象实例的类型,例如:

接口组合

我们可以将上面的IFile接口写成这个样子

Any类型

由于Go语言中任何对象实例都满足空接口interface{},所以interface{}看起来像是可以指向任何对象的Any类型,如下:

Golang 入门(六) —— 面向对象编程

Go语言的面向对象设计的非常简洁。简洁在于Go语言并没有沿袭传统面向对象编程中的诸多概念,比如继承、虚函数、构造函数、析构函数、隐藏this指针等。

1、类型系统

类型系统是指一个语言的类型体系结构。一个典型的类型系统通常包含如下基本内容:

  • 基础类型:如byteintboolfloat
  • 复合类型:如数组、结构体、指针等
  • 可以指向任意对象的类型Any类型
  • 值语义和引用语义
  • 面向对象,即所有具备面向对象特征的类型
  • 接口

在Go语言中,可以为任意类型(包括内置类型、但不包括指针类型)添加相应的方法。例如:

在这个例子中,我们定义了一个类型Integer,他和int没有本质的区别,只是它为内置的int类型增加了一个新的方法Less()

在Go语言中没有隐藏的this指针:

  • 方法施加的目标(也就是对象)显示传递,没有被隐藏起来
  • 方法施加的目标(也就是对象)不需要非得是指针,也不用非得是this

php中,使用写一段差不多意思的例子。

初学者可能会比较难离理解其背后的机制,一直this到底从何而来。其实能是php自动隐藏了this指针,如果不隐藏的话,代码可能会这个样子:

Go语言就非常直观的表现出来了,我们重写一下上面的例子:

就会发现,原来this是这么过来的。

如果要求对象必须以指针形式传递,这有时会是个额外的成本,因为对象有时很小(比如4字节),用指针传递并不划算。

只有在你需要修改对象的时候,才必须使用指针。它不是Go语言的约束,而是一种自然的约束。
举个例子:

2、值语义和引用语义

值语义和引用语义的差别在于赋值,比如下面的例子:

如果b的修改没有影响到a,那么此类型属于值类型,反之为引用类型。

Go语言中的大多数类型都是值语义,包括:

  • 基本类型:byteintfloat32float64string
  • 符合类型:arraystructpointer

Go语言中的数组和基本类型没有什么区别,是很纯粹的值类型。

如果希望完全复制,需要用到指针

Go语言中有4个类型比较特别,看起来像引用类型,

  • 切片: 指向数组的一个区间
  • map: 极其常见的数据结构,提供键值查询能力
  • channel: 执行体goroutine间的通信设施
  • 结构: 对一组满足某个且越的类型的抽象

但是这并不影响我们将Go语言看作值语义。

切片本事上是一个区间,大致可以将[]T表示为:

因为数组切片内部是指针,所以可以改变所指向的数组元素并不奇怪。切片类型本身的复制仍然是值语义。

map本质上是一个字典指针,你可以大致将map[K]V表示为:

基于指针,我们完全可以创建一个引用类型,如:

channelmap类似,本质上是一个指针。将他们设计为引用类型的原因是,完整复制一个channelmap并不是常规需求。

同样,接口具备引用语义,是因为维持了两个指针,示意为:

3、结构体

Go语言的结构体和其他语言的类有同等的地位,但是Go语言放弃了包括继承在内的大量面向对象特性,只保留了组合这个最基础的特新。

上面我们说道,所有的Go语言类型(指针除外)都可以有自己的方法。在这个背景下,Go语言的结构体只是很普通的复合类型,平淡无奇。

定义了Rect类型后,该如何创建初始化对象实例呢?

Go语言中未进行显示初始化的变量都为被初始化为该类型的零值。

Go语言虽然不支持继承,但是我们可以采用组合的方式,来完成继承所做的事情,我们将其称之为匿名组合:

以上代码实现了Foo继承了Base,并且Foo重写BaseBar方法。

在Go语言官方网站提供了Effective Go中提到一个匿名组合的例子:

在适合的赋值后,我们在Job类型的所有成员方法中可以很舒适的借用log.Logger提供的方法。

对Job的实现者来说,根本就不需要意识到log.Logger的存在,这既是匿名组合的魅力所在。

Golang 入门(五) —— 错误处理

1、error接口

Go语言引入了一个关于错误处理的标准模式,即error接口,该接口的定义如下:

对于大多数函数,如果需要返回错误,大致可以定义如下模式,将error作为多返回值中的最后一个即可,但是这并非是强制要求:

需要时,可以如下调用:

下面使用Go库中的实际代码来示范如何自定义error类型。

调用可以如下:

2、defer

关键字defer允许我们推迟到函数返回之前(或任意位置执行 return 语句之后)一刻才执行某个语句或函数(为什么要在返回之后才执行这些语句?因为 return 语句同样可以包含一些操作,而不是单纯地返回某个值)。

关键字 defer 的用法类似于面向对象编程语言 JavaC#finally 语句块,它一般用于释放某些已分配的资源。

下面的例子可以简单的解释defer的用法,

如果一句话干不完清理的工作,可以使用在defer后加一个匿名函数的做法:

另外,一个函数里可以存在多个defer,因此需要注意的是,defer语句遵循先进后出的原则,即最后一个defer第一个执行,第一个defer最后一个执行。

3、panicrecover

Go语言引入了两个内置函数panic()recover(),用以报告和处理运行时错误和程序中的错误场景:

使用方法如下:

Golang 入门(四) —— 函数

在Go语言中,函数的基本组成为:关键字func、函数名、参数列表、返回值、函数体和返回语句。

1、函数定义

ab为参数
reserr为返回值
当然返回值也可以不写变量名称。

2、函数调用

函数调用非常方便,只要事先导入该函数的包,就可以直接调用函数了:

需要牢记规则:小写字母开头的函数只在本包内可见,大写字母开头的函数才能被其他包使用。这个规则也是用于变量的可见性。

3、不定参数

  • 不定参数类型

这个方法我们可以个通过如下方式来调用:

形如...type格式的类型只能作为函数的参数类型存在,并且必须是最后一个参数。它是一个语法糖syntactic sugar,即这种语法对语言的功能没有影响,但是更方便程序员使用。

用内部机制上来说,类型...type本质上是一个切片,也就是[]type,这也就是为什么上面的参数args可以用for循环来获取每个传入的参数。

调用方式:

  • 不定参数的传递

我们可以使用如下方式调用myFunc

  • 任意类型的不定参数

在之前例子中将不定参数的类型约束为int,如果你希望传任意类型,可以指定类型为interface{}
下面是Go语言标准库中的fmt.Printf()函数原型:

4、多返回值

Go语言的函数或者成员的方法可以有多个返回值,这个特性能够使我们写出比其他语言更优雅,更简洁的代码。比如,File.read()函数就可以同时返回读取的字节数和错误信息。如果文件读取成功者返回值中的n为读取字节数,errnil,否则err为具体的出错信息。

5、匿名函数与闭包

  • 匿名函数

匿名函数可以赋值给一个变量,也可以直接执行:

  • 闭包

基本概念:闭包是可以包含自由(未绑定到特定对象)变量的代码块,这些变量不再这个代码块内或者任何全局上下文中定义,而是在定义代码块的环境中定义。要执行的代码块为自由变量提供绑定的计算环境(作用域)。

闭包的价值:闭包的价值在于可以作为函数对象或者匿名函数,对于类型系统而言,这意味着不仅要表示数据还要表示代码。支持闭包的多数语言都将函数作为第一级对象,就是说这些函数可以存储到变量中作为参数传递给其他函数。

Go语言中的闭包:如下,