go语言总结 go语言示例

Go语言和其他语言的不同之基本语法Go语言作为出现比较晚的一门编程语言go语言总结,在其原生支持高并发、云原生等领域的优秀表现,像目前比较流行的容器编排技术Kubernetes、容器技术Docker都是用Go语言写的,像Java等其他面向对象的语言,虽然也能做云原生相关的开发 , 但是支持的程度远没有Go语言高,凭借其语言特性和简单的编程方式,弥补go语言总结了其他编程语言一定程度上的不足,一度成为一个热门的编程语言 。
最近在学习Go语言,go语言总结我之前使用过C#、Java等面向对象编程的语言,发现其中有很多的编程方式和其他语言有区别的地方,好记性不如烂笔头,总结一下,和其他语言做个对比 。这里只总结差异的地方,具体的语法不做详细的介绍 。
种一棵树最好的时间是十年前,其次是现在 。
3)变量初始化时候可以和其他语言一样直接在变量后面加等号,等号后面为要初始化的值,也可以使用变量名:=变量值的简单方式
3)变量赋值 Go语言的变量赋值和多数语言一致 , 但是Go语言提供了多重赋值的功能,比如下面这个交换i、j变量的语句:
在不支持多重赋值的语言中,交换两个变量的值需要引入一个中间变量:
4)匿名变量
在使用其他语言时 , 有时候要获取一个值,却因为该函数返回多个值而不得不定义很多没有的变量,Go语言可以借助多重返回值和匿名变量来避免这种写法,使代码看起来更优雅 。
假如GetName()函数返回3个值 , 分别是firstName,lastName和nickName
若指向获得nickName , 则函数调用可以这样写
这种写法可以让代码更清晰,从而大幅降低沟通的复杂度和维护的难度 。
1)基本常量
常量使用关键字const 定义,可以限定常量类型,但不是必须的 , 如果没有定义常量的类型,是无类型常量
2)预定义常量
Go语言预定义了这些常量 true、false和iota
iota比较特殊,可以被任务是一个可被编译器修改的常量,在每个const关键字出现时被重置为0,然后在下一个const出现之前每出现一个iota,其所代表的数字会自动加1.
3)枚举
1)int 和int32在Go语言中被认为是两种不同类型的类型
2)Go语言定义了两个浮点型float32和float64,其中前者等价于C语言的float类型 , 后者等价于C语言的double类型
3)go语言支持复数类型
复数实际上是由两个实数(在计算机中使用浮点数表示)构成,一个表示实部(real)、一个表示虚部(imag) 。也就是数学上的那个复数
复数的表示
实部与虚部
对于一个复数z=complex(x,y),就可以通过Go语言内置函数real(z)获得该复数的实部,也就是x , 通过imag(z)获得该复数的虚部,也就是y
4)数组(值类型,长度在定义后无法再次修改 , 每次传递都将产生一个副本 。)
5)数组切片(slice)
数组切片(slice)弥补了数组的不足,其数据结构可以抽象为以下三个变量:
6)Map 在go语言中Map不需要引入任何库,使用很方便
Go循环语句只支持for关键字,不支持while和do-while
goto语句的语义非常简单,就是跳转到本函数内的某个标签
今天就介绍到这里,以后go语言总结我会在总结Go语言在其他方面比如并发编程、面向对象、网络编程等方面的不同及使用方法 。希望对大家有所帮助 。
为什么go语言适合开发网游服务器端个人觉得golang十分适合进行网游服务器端开发,写下这篇文章总结一下 。从网游的角度看:要成功的运营一款网游 , 很大程度上依赖于玩家自发形成的社区 。只有玩家自发形成一个稳定的生态系统,游戏才能持续下去,避免鬼城的出现 。而这就需要多次大量导入用户,在同时在线用户量达到某个临界点的时候 , 才有可能完成 。因此 , 多人同时在线十分有必要 。再来看网游的常见玩法,除了排行榜这类统计和数据汇总的功能外,基本没有需要大量CPU时间的应用 。以前的项目里 , 即时战斗产生的各种伤害计算对CPU的消耗也不大 。玩家要完成一次操作,需要通过客户端-服务器端-客户端这样一个来回 , 为了获得高响应速度 , 满足玩家体验,服务器端的处理也不能占用太多时间 。所以,每次请求对应的CPU占用是比较小的 。网游的IO主要分两个方面,一个是网络IO,一个是磁盘IO 。网络IO方面,可以分成美术资源的IO和游戏逻辑指令的IO,这里主要分析游戏逻辑的IO 。游戏逻辑的IO跟CPU占用的情况相似 , 每次请求的字节数很小 , 但由于多人同时在线,因此并发数相当高 。另外,地图信息的广播也会带来比较频繁的网络通信 。磁盘IO方面,主要是游戏数据的保存 。采用不同的数据库 , 会有比较大的区别 。以前的项目里 , 就经历了从MySQL转向MongoDB这种内存数据库的过程,磁盘IO不再是瓶颈 。总体来说,还是用内存做一级缓冲,避免大量小数据块读写的方案 。针对网游的这些特点,golang的语言特性十分适合开发游戏服务器端 。首先,go语言提供goroutine机制作为原生的并发机制 。每个goroutine所需的内存很少,实际应用中可以启动大量的goroutine对并发连接进行响应 。goroutine与gevent中的greenlet很相像,遇到IO阻塞的时候,调度器就会自动切换到另一个goroutine执行,保证CPU不会因为IO而发生等待 。而goroutine与gevent相比,没有了python底层的GIL限制,就不需要利用多进程来榨取多核机器的性能了 。通过设置最大线程数,可以控制go所启动的线程,每个线程执行一个goroutine,让CPU满负载运行 。同时,go语言为goroutine提供了独到的通信机制channel 。channel发生读写的时候,也会挂起当前操作channel的goroutine , 是一种同步阻塞通信 。这样既达到了通信的目的,又实现同步,用CSP模型的观点看,并发模型就是通过一组进程和进程间的事件触发解决任务的 。虽然说,主流的编程语言之间,只要是图灵完备的 , 他们就都能实现相同的功能 。但go语言提供的这种协程间通信机制 , 十分优雅地揭示了协程通信的本质,避免了以往锁的显式使用带给程序员的心理负担,确是一大优势 。进行网游开发的程序员,可以将游戏逻辑按照单线程阻塞式的写,不需要额外考虑线程调度的问题,以及线程间数据依赖的问题 。因为,线程间的channel通信 , 已经表达了线程间的数据依赖关系了,而go的调度器会给予妥善的处理 。另外,go语言提供的gc机制 , 以及对指针的保护式使用,可以大大减轻程序员的开发压力,提高开发效率 。展望未来,我期待go语言社区能够提供更多的goroutine间的隔离机制 。个人十分推崇erlang社区的脆崩哲学,推动应用发生预期外行为时,尽早崩溃,再fork出新进程处理新的请求 。对于协程机制,需要由程序员保证执行的函数不会发生死循环,导致线程卡死 。
Go语言设计与实现(上)基本设计思路:
类型转换、类型断言、动态派发 。iface,eface 。
反射对象具有的方法:
编译优化:
内部实现:
实现 Context 接口有以下几个类型(空实现就忽略了):
互斥锁的控制逻辑:
设计思路:
(以上为写被读阻塞 , 下面是读被写阻塞)
总结,读写锁的设计还是非常巧妙的:
设计思路:
WaitGroup 有三个暴露的函数:
部件:
设计思路:
结构:
Once 只暴露了一个方法:
实现:
三个关键点:
细节:
让多协程任务的开始执行时间可控(按顺序或归一) 。(Context 是控制结束时间)
设计思路: 通过一个锁和内置的 notifyList 队列实现,Wait() 会生成票据 , 并将等待协程信息加入链表中 , 等待控制协程中发送信号通知一个(Signal())或所有(Boardcast())等待者(内部实现是通过票据通知的)来控制协程解除阻塞 。
暴露四个函数:
实现细节:
部件:
包: golang.org/x/sync/errgroup
作用:开启func() error函数签名的协程 , 在同 Group 下协程并发执行过程并收集首次 err 错误 。通过 Context 的传入,还可以控制在首次 err 出现时就终止组内各协程 。
设计思路:
结构:
暴露的方法:
实现细节:
注意问题:
包: "golang.org/x/sync/semaphore"
作用:排队借资源(如钱,有借有还)的一种场景 。此包相当于对底层信号量的一种暴露 。
设计思路:有一定数量的资源 Weight,每一个 waiter 携带一个 channel 和要借的数量 n 。通过队列排队执行借贷 。
结构:
暴露方法:
细节:
部件:
细节:
包: "golang.org/x/sync/singleflight"
作用:防击穿 。瞬时的相同请求只调用一次,response 被所有相同请求共享 。
设计思路:按请求的 key 分组(一个 *call 是一个组,用 map 映射存储组),每个组只进行一次访问,组内每个协程会获得对应结果的一个拷贝 。
结构:
逻辑:
细节:
部件:
如有错误,请批评指正 。
Go 语言的错误处理机制是一个优秀的设计吗这个问题说来话长,我先表达一下我的观点 , Go语言从语法层面提供区分错误和异常的机制是很好的做法,比自己用单个返回值做值判断要方便很多 。
上面看到很多知乎大牛把异常和错误混在一起说 , 有认为Go没有异常机制的,有认为Go纯粹只有异常机制的,我觉得这些观点都太片面go语言总结了 。
具体对于错误和异常的讨论,我转发一下前阵子写的一篇日志抛砖引玉吧 。
============================
最近连续遇到朋友问我项目里错误和异常管理的事情,之前也多次跟团队强调过错误和异常管理的一些概念,所以趁今天有动力就赶紧写一篇Go语言项目错误和异常管理的经验分享 。
首先我们要理清:什么是错误、什么是异常、为什么需要管理 。然后才是怎样管理 。
错误和异常从语言机制上面讲,就是error和panic的区别,放到别的语言也一样,别的语言没有error类型,但是有错误码之类的,没有panic,但是有throw之类的 。
在语言层面它们是两种概念,导致的是两种不同的结果 。如果程序遇到错误不处理,那么可能进一步的产生业务上的错误 , 比如给用户多扣钱了,或者进一步产生了异常go语言总结;如果程序遇到异常不处理,那么结果就是进程异常退出 。
在项目里面是不是应该处理所有的错误情况和捕捉所有的异常呢?我只能说,go语言总结你可以这么做,但是估计效果不会太好 。我的理由是:
如果所有东西都处理和记录,那么重要信息可能被淹没在信息的海洋里 。
不应该处理的错误被处理了,很容易导出BUG暴露不出来,直到出现更严重错误的时候才暴露出问题 , 到时候排查就很困难了,因为已经不是错误的第一现场 。
所以错误和异常最好能按一定的规则进行分类和管理,在第一时间能暴露错误和还原现场 。
对于错误处理,Erlang有一个很好的概念叫速错,就是有错误第一时间暴露它 。我们的项目从Erlang到Go一直是沿用这一设计原则 。但是应用这个原则的前提是先得区分错误和异常这两个概念 。
错误和异常上面已经提到了 , 从语言机制层面比较容易区分它们,但是语言取决于人为,什么情况下用错误表达,什么情况下用异常表达 , 就得有一套规则,否则很容易出现全部靠异常来做错误处理的情况 , 似乎Java项目特别容易出现这样的设计 。
这里我先假想有这样一个业务:游戏玩家通过购买按钮,用铜钱购买宝石 。
在实现这个业务的时候,程序逻辑会进一步分化成客户端逻辑和服务端逻辑 , 客户端逻辑又进一步因为设计方式的不同分化成两种结构:胖客户端结构、瘦客户端结构 。
胖客户端结构,有更多的本地数据和懂得更多的业务逻辑,所以在胖客户端结构的应用中,以上的业务会实现成这样:客户端检查缓存中的铜钱数量,铜钱数量足够的时候购买按钮为可用的亮起状态,用户点击购买按钮后客户端发送购买请求到服务端;服务端收到请求后校验用户的铜钱数量,如果铜钱数量不足就抛出异常,终止请求过程并断开客户端的连接,如果铜钱数量足够就进一步完成宝石购买过程,这里不继续描述正常过程 。
因为正常的客户端是有一步数据校验的过程的,所以当服务端收到不合理的请求(铜钱不足以购买宝石)时 , 抛出异常比返回错误更为合理,因为这个请求只可能来自两种客户端:外挂或者有BUG的客户端 。如果不通过抛出异常来终止业务过程和断开客户端连接,那么程序的错误就很难被第一时间发现,攻击行为也很难被发现 。
我们再回头看瘦客户端结构的设计,瘦客户端不会存有太多状态数据和用户数据也不清楚业务逻辑,所以客户端的设计会是这样:用户点击购买按钮,客户端发送购买请求;服务端收到请求后检查铜钱数量,数量不足就返回数量不足的错误码,数量足够就继续完成业务并返回成功信息;客户端收到服务端的处理结果后,在界面上做出反映 。
在这种结构下,铜钱不足就变成了业务逻辑范围内的一种失败情况,但不能提升为异常 , 否则铜钱不足的用户一点购买按钮都会出错掉线 。
所以,异常和错误在不同程序结构下是互相转换的,我们没办法一句话的给所有类型所有结构的程序一个统一的异常和错误分类规则 。
但是,异常和错误的分类是有迹可循的 。比如上面提到的痩客户端结构,铜钱不足是业务逻辑范围内的一种失败情况,它属于业务错误,再比如程序逻辑上尝试请求某个URL,最多三次,重试三次的过程中请求失败是错误,重试到第三次 , 失败就被提升为异常了 。
所以我们可以这样来归类异常和错误:不会终止程序逻辑运行的归类为错误,会终止程序逻辑运行的归类为异常 。
因为错误不会终止逻辑运行,所以错误是逻辑的一部分 , 比如上面提到的瘦客户端结构,铜钱不足的错误就是业务逻辑处理过程中需要考虑和处理的一个逻辑分支 。而异常就是那些不应该出现在业务逻辑中的东西,比如上面提到的胖客户端结构,铜钱不足已经不是业务逻辑需要考虑的一部分了,所以它应该是一个异常 。
错误和异常的分类需要通过一定的思维训练来强化分类能力,就类似于面向对象的设计方式一样的,技术实现就摆在那边,但是要用好需要不断的思维训练不断的归类和总结,以上提到的归类方式希望可以作为一个参考,期待大家能发现更多更有效的归类方式 。
接下来我们讲一下速错和Go语言里面怎么做到速错 。
速错我最早接触是在做的时候就体验到的,当然跟Erlang的速错不完全一致 , 那时候也没有那么高大上的一个名字,但是对待异常的理念是一样的 。
在.NET项目开发的时候,有经验的程序员都应该知道 , 不能随便re-throw,就是catch错误再抛出,原因是异常的第一现场会被破坏,堆栈跟踪信息会丢失,因为外部最后拿到异常的堆栈跟踪信息,是最后那次throw的异常的堆栈跟踪信息;其次,不能随便try catch , 随便catch很容易导出异常暴露不出来,升级为更严重的业务漏洞 。
到了Erlang时期,大家学到了速错概念,简单来讲就是:让它挂 。只有挂了go语言总结你才会第一时间知道错误,但是Erlang的挂,只是Erlang进程的异常退出,不会导致整个Erlang节点退出,所以它挂的影响层面比较低 。
在Go语言项目中,虽然有类似Erlang进程的Goroutine,但是Goroutine如果panic了 , 并且没有recover,那么整个Go进程就会异常退出 。所以我们在Go语言项目中要应用速错的设计理念,就要对Goroutine做一定的管理 。
在我们的游戏服务端项目中,我把Goroutine按挂掉后的结果分为两类:1、挂掉后不影响其go语言总结他业务或功能的;2、挂掉后业务就无法正常进行的 。
第一类Goroutine典型的有:处理各个玩家请求的Goroutine , 因为每个玩家连接各自有一个Goroutine , 所以挂掉了只会影响单个玩家,不会影响整体业务进行 。
第二类Goroutine典型的有:数据库同步用的Goroutine,如果它挂了,数据就无法同步到数据库,游戏如果继续运行下去只会导致数据回档,还不如让整个游戏都异常退出 。
这样一分类,就可以比较清楚哪些Goroutine该做recover处理,哪些不该做recover处理了 。
那么在做recover处理时,要怎样才能尽量保留第一现场来帮组开发者排查问题原因呢?我们项目中通常是会在最外层的recover中把错误和堆栈跟踪信息记进日志,同时把关键的业务信息 , 比如:用户ID、来源IP、请求数据等也一起记录进去 。
为此,我们还特地设计了一个库,用来格式化输出堆栈跟踪信息和对象信息,项目地址:funny/debug · GitHub
通篇写下来发现比我预期的长很多,所以这里我做一下归纳总结,帮组大家理解这篇文章所要表达的:
错误和异常需要分类和管理,不能一概而论
错误和异常的分类可以以是否终止业务过程作为标准
错误是业务过程的一部分 , 异常不是
不要随便捕获异常 , 更不要随便捕获再重新抛出异常
Go语言项目需要把Goroutine分为两类,区别处理异常
在捕获到异常时,需要尽可能的保留第一现场的关键数据
以上仅为一家之言,抛砖引玉,希望对大家有所帮助 。
一学就会,手把手教你用Go语言调用智能合约智能合约调用是实现一个 DApp 的关键,一个完整的 DApp 包括前端、后端、智能合约及区块 链系统,智能合约的调用是连接区块链与前后端的关键 。
我们先来了解一下智能合约调用的基础原理 。智能合约运行在以太坊节点的 EVM 中 。因此要 想调用合约必须要访问某个节点 。
以后端程序为例,后端服务若想连接节点有两种可能,一种是双 方在同一主机 , 此时后端连接节点可以采用 本地 IPC(Inter-Process Communication,进 程间通信)机制,也可以采用 RPC(Remote Procedure Call,远程过程调用)机制;另 一种情况是双方不在同一台主机,此时只能采用 RPC 机制进行通信 。
提到 RPC, 读者应该对 Geth 启动参数有点印象,Geth 启动时可以选择开启 RPC 服务 , 对应的 默认服务端口是 8545 。。
接着 , 我们来了解一下智能合约运行的过程 。
智能合约的运行过程是后端服务连接某节点 , 将 智能合约的调用(交易)发送给节点,节点在验证了交易的合法性后进行全网广播,被矿工打包到 区块中代表此交易得到确认,至此交易才算完成 。
就像数据库一样,每个区块链平台都会提供主流 开发语言的 SDK(Software Development Kit,软件开发工具包),由于 Geth 本身就是用 Go 语言 编写的,因此若想使用 Go 语言连接节点、发交易 , 直接在工程内导入 go-ethereum(Geth 源码) 包就可以了,剩下的问题就是流程和 API 的事情了 。
总结一下,智能合约被调用的两个关键点是节点和 SDK 。
由于 IPC 要求后端与节点必须在同一主机,所以很多时候开发者都会采用 RPC 模式 。除了 RPC,以太坊也为开发者提供了 json- rpc 接口,本文就不展开讨论了 。
接下来介绍如何使用 Go 语言,借助 go-ethereum 源码库来实现智能合约的调用 。这是有固定 步骤的,我们先来说一下总体步骤,以下面的合约为例 。
步骤 01:编译合约,获取合约 ABI(Application Binary Interface,应用二进制接口) 。单击【ABI】按钮拷贝合约 ABI 信息 , 将其粘贴到文件 calldemo.abi 中(可使用 Go 语言IDE 创建该文件,文件名可自定义 , 后缀最好使用 abi) 。
最好能将 calldemo.abi 单独保存在一个目录下,输入“ls”命令只能看到 calldemo.abi 文件,参 考效果如下:
步骤 02:获得合约地址 。注意要将合约部署到 Geth 节点 。因此 Environment 选择为 Web3 Provider 。
在【Environment】选项框中选择“Web3 Provider”,然后单击【Deploy】按钮 。
部署后,获得合约地址为:0xa09209c28AEf59a4653b905792a9a910E78E7407 。
步骤 03:利用 abigen 工具(Geth 工具包内的可执行程序)编译智能合约为 Go 代码 。abigen 工具的作用是将 abi 文件转换为 Go 代码,命令如下:
其中各参数的含义如下 。(1)abi:是指定传入的 abi 文件 。(2)type:是指定输出文件中的基本结构类型 。(3)pkg:指定输出文件 package 名称 。(4)out:指定输出文件名 。执行后,将在代码目录下看到 funcdemo.go 文件,读者可以打开该文件欣赏一下,注意不要修改它 。
步骤 04:创建 main.go,填入如下代码 。注意代码中 HexToAddress 函数内要传入该合约部署后的地址,此地址在步骤 01 中获得 。
步骤 04:设置 go mod,以便工程自动识别 。
前面有所提及,若要使用 Go 语言调用智能合约,需要下载 go-ethereum 工程,可以使用下面 的指令:
该指令会自动将 go-ethereum 下载到“$GOPATH/src/github.com/ethereum/go-ethereum” , 这样还算 不错 。不过 , Go 语言自 1.11 版本后,增加了 module 管理工程的模式 。只要设置好了 go mod , 下载 依赖工程的事情就不必关心了 。
接下来设置 module 生效和 GOPROXY , 命令如下:
在项目工程内,执行初始化 , calldemo 可以自定义名称 。
步骤 05:运行代码 。执行代码 , 将看到下面的效果,以及最终输出的 2020 。
上述输出信息中,可以看到 Go 语言会自动下载依赖文件 , 这就是 go mod 的神奇之处 。看到 2020,相信读者也知道运行结果是正确的了 。
【go语言总结 go语言示例】关于go语言总结和go语言示例的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站 。

    推荐阅读