十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
是程序运行太快肉眼分辨不出了,可以睡眠5秒
公司主营业务:成都网站建设、网站设计、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。成都创新互联是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。成都创新互联推出天台免费做网站回馈大家。
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("hello world !")
time.Sleep(5 * time.Second)
}
应用程序发生异常 未知的软件异常
1.病毒木马造成的,在当今互联网时代,病毒坐着为了获得更多的牟利,常用病毒绑架应用程序和系统文件,然后某些安全杀毒软件把被病毒木马感染的应用程序和系统文件当病毒杀了导致的。
2.应用程序组件丢失,应用程序完整的运行需要一些系统文件或者某些ll文件支持的,如果应用程序组件不完整也会导致的。
3.系统文件损坏或丢失,盗版系统或Ghost版本系统,很容易出现该问题。
4.操作系统自身的问题,操作系统本身也会有bug 。
5.硬件问题,例如内存条坏了或者存在质量问题,或者内存条的金手指的灰尘特别多。
应用程序发生异常怎么办
1.检查电脑是否存在病毒,请使用百度卫士进行木马查杀。
2.系统文件损坏或丢失,盗版系统或Ghost版本系统,很容易出现该问题。建议:使用完整版或正版系统。
3.安装的软件与系统或其它软件发生冲突,找到发生冲突的软件,卸载它。如果更新下载补丁不是该软件的错误补丁,也会引起软件异常,解决办法:卸载该软件,重新下载重新安装试试。顺便检查开机启动项,把没必要启动的启动项禁止开机启动。
4.如果检查上面的都没问题,可以试试下面的方法。
打开开始菜单→运行→输入cmd→回车,在命令提示符下输入下面命令 for %1 in (%windir%\system32\*.dll) do regsvr32.exe /s %1回车。
完成后,在输入下面
for %i in (%windir%\system32\*.ocx) do regsvr32.exe /s %i 回车。
如果怕输入错误,可以复制这两条指令,然后在命令提示符后击鼠标右键,打“粘贴”,回车,耐心等待,直到屏幕滚动停止为止。(重启电脑)。
一开始你只有一个主协程,如果子协程不启动,即便主协程让出了时间片,也没有可运行的子协程啊。交换顺序就是起到了先启动子协程的作用。
这个问题说来话长,我先表达一下我的观点,Go语言从语法层面提供区分错误和异常的机制是很好的做法,比自己用单个返回值做值判断要方便很多。
上面看到很多知乎大牛把异常和错误混在一起说,有认为Go没有异常机制的,有认为Go纯粹只有异常机制的,我觉得这些观点都太片面了。
具体对于错误和异常的讨论,我转发一下前阵子写的一篇日志抛砖引玉吧。
============================
最近连续遇到朋友问我项目里错误和异常管理的事情,之前也多次跟团队强调过错误和异常管理的一些概念,所以趁今天有动力就赶紧写一篇Go语言项目错误和异常管理的经验分享。
首先我们要理清:什么是错误、什么是异常、为什么需要管理。然后才是怎样管理。
错误和异常从语言机制上面讲,就是error和panic的区别,放到别的语言也一样,别的语言没有error类型,但是有错误码之类的,没有panic,但是有throw之类的。
在语言层面它们是两种概念,导致的是两种不同的结果。如果程序遇到错误不处理,那么可能进一步的产生业务上的错误,比如给用户多扣钱了,或者进一步产生了异常;如果程序遇到异常不处理,那么结果就是进程异常退出。
在项目里面是不是应该处理所有的错误情况和捕捉所有的异常呢?我只能说,你可以这么做,但是估计效果不会太好。我的理由是:
如果所有东西都处理和记录,那么重要信息可能被淹没在信息的海洋里。
不应该处理的错误被处理了,很容易导出BUG暴露不出来,直到出现更严重错误的时候才暴露出问题,到时候排查就很困难了,因为已经不是错误的第一现场。
所以错误和异常最好能按一定的规则进行分类和管理,在第一时间能暴露错误和还原现场。
对于错误处理,Erlang有一个很好的概念叫速错,就是有错误第一时间暴露它。我们的项目从Erlang到Go一直是沿用这一设计原则。但是应用这个原则的前提是先得区分错误和异常这两个概念。
错误和异常上面已经提到了,从语言机制层面比较容易区分它们,但是语言取决于人为,什么情况下用错误表达,什么情况下用异常表达,就得有一套规则,否则很容易出现全部靠异常来做错误处理的情况,似乎Java项目特别容易出现这样的设计。
这里我先假想有这样一个业务:游戏玩家通过购买按钮,用铜钱购买宝石。
在实现这个业务的时候,程序逻辑会进一步分化成客户端逻辑和服务端逻辑,客户端逻辑又进一步因为设计方式的不同分化成两种结构:胖客户端结构、瘦客户端结构。
胖客户端结构,有更多的本地数据和懂得更多的业务逻辑,所以在胖客户端结构的应用中,以上的业务会实现成这样:客户端检查缓存中的铜钱数量,铜钱数量足够的时候购买按钮为可用的亮起状态,用户点击购买按钮后客户端发送购买请求到服务端;服务端收到请求后校验用户的铜钱数量,如果铜钱数量不足就抛出异常,终止请求过程并断开客户端的连接,如果铜钱数量足够就进一步完成宝石购买过程,这里不继续描述正常过程。
因为正常的客户端是有一步数据校验的过程的,所以当服务端收到不合理的请求(铜钱不足以购买宝石)时,抛出异常比返回错误更为合理,因为这个请求只可能来自两种客户端:外挂或者有BUG的客户端。如果不通过抛出异常来终止业务过程和断开客户端连接,那么程序的错误就很难被第一时间发现,攻击行为也很难被发现。
我们再回头看瘦客户端结构的设计,瘦客户端不会存有太多状态数据和用户数据也不清楚业务逻辑,所以客户端的设计会是这样:用户点击购买按钮,客户端发送购买请求;服务端收到请求后检查铜钱数量,数量不足就返回数量不足的错误码,数量足够就继续完成业务并返回成功信息;客户端收到服务端的处理结果后,在界面上做出反映。
在这种结构下,铜钱不足就变成了业务逻辑范围内的一种失败情况,但不能提升为异常,否则铜钱不足的用户一点购买按钮都会出错掉线。
所以,异常和错误在不同程序结构下是互相转换的,我们没办法一句话的给所有类型所有结构的程序一个统一的异常和错误分类规则。
但是,异常和错误的分类是有迹可循的。比如上面提到的痩客户端结构,铜钱不足是业务逻辑范围内的一种失败情况,它属于业务错误,再比如程序逻辑上尝试请求某个URL,最多三次,重试三次的过程中请求失败是错误,重试到第三次,失败就被提升为异常了。
所以我们可以这样来归类异常和错误:不会终止程序逻辑运行的归类为错误,会终止程序逻辑运行的归类为异常。
因为错误不会终止逻辑运行,所以错误是逻辑的一部分,比如上面提到的瘦客户端结构,铜钱不足的错误就是业务逻辑处理过程中需要考虑和处理的一个逻辑分支。而异常就是那些不应该出现在业务逻辑中的东西,比如上面提到的胖客户端结构,铜钱不足已经不是业务逻辑需要考虑的一部分了,所以它应该是一个异常。
错误和异常的分类需要通过一定的思维训练来强化分类能力,就类似于面向对象的设计方式一样的,技术实现就摆在那边,但是要用好需要不断的思维训练不断的归类和总结,以上提到的归类方式希望可以作为一个参考,期待大家能发现更多更有效的归类方式。
接下来我们讲一下速错和Go语言里面怎么做到速错。
速错我最早接触是在做的时候就体验到的,当然跟Erlang的速错不完全一致,那时候也没有那么高大上的一个名字,但是对待异常的理念是一样的。
在.NET项目开发的时候,有经验的程序员都应该知道,不能随便re-throw,就是catch错误再抛出,原因是异常的第一现场会被破坏,堆栈跟踪信息会丢失,因为外部最后拿到异常的堆栈跟踪信息,是最后那次throw的异常的堆栈跟踪信息;其次,不能随便try catch,随便catch很容易导出异常暴露不出来,升级为更严重的业务漏洞。
到了Erlang时期,大家学到了速错概念,简单来讲就是:让它挂。只有挂了你才会第一时间知道错误,但是Erlang的挂,只是Erlang进程的异常退出,不会导致整个Erlang节点退出,所以它挂的影响层面比较低。
在Go语言项目中,虽然有类似Erlang进程的Goroutine,但是Goroutine如果panic了,并且没有recover,那么整个Go进程就会异常退出。所以我们在Go语言项目中要应用速错的设计理念,就要对Goroutine做一定的管理。
在我们的游戏服务端项目中,我把Goroutine按挂掉后的结果分为两类:1、挂掉后不影响其他业务或功能的;2、挂掉后业务就无法正常进行的。
第一类Goroutine典型的有:处理各个玩家请求的Goroutine,因为每个玩家连接各自有一个Goroutine,所以挂掉了只会影响单个玩家,不会影响整体业务进行。
第二类Goroutine典型的有:数据库同步用的Goroutine,如果它挂了,数据就无法同步到数据库,游戏如果继续运行下去只会导致数据回档,还不如让整个游戏都异常退出。
这样一分类,就可以比较清楚哪些Goroutine该做recover处理,哪些不该做recover处理了。
那么在做recover处理时,要怎样才能尽量保留第一现场来帮组开发者排查问题原因呢?我们项目中通常是会在最外层的recover中把错误和堆栈跟踪信息记进日志,同时把关键的业务信息,比如:用户ID、来源IP、请求数据等也一起记录进去。
为此,我们还特地设计了一个库,用来格式化输出堆栈跟踪信息和对象信息,项目地址:funny/debug · GitHub
通篇写下来发现比我预期的长很多,所以这里我做一下归纳总结,帮组大家理解这篇文章所要表达的:
错误和异常需要分类和管理,不能一概而论
错误和异常的分类可以以是否终止业务过程作为标准
错误是业务过程的一部分,异常不是
不要随便捕获异常,更不要随便捕获再重新抛出异常
Go语言项目需要把Goroutine分为两类,区别处理异常
在捕获到异常时,需要尽可能的保留第一现场的关键数据
以上仅为一家之言,抛砖引玉,希望对大家有所帮助。
英文原文链接【Go, the unwritten parts】 发表于2017/05/22 作者JBD是Go语言开发小组成员
检查程序的执行路径和当前状态是非常有用的调试手段。核心文件(core file)包含了一个运行进程的内存转储和状态。它主要是用来作为事后调试程序用的。它也可以被用来查看一个运行中的程序的状态。这两个使用场景使调试文件转储成为一个非常好的诊断手段。我们可以用这个方法来做事后诊断和分析线上的服务(production services)。
在这篇文章中,我们将用一个简单的hello world网站服务作为例子。在现实中,我们的程序很容易就会变得很复杂。分析核心转储给我们提供了一个机会去重构程序的状态并且查看只有在某些条件/环境下才能重现的案例。
作者注 : 这个调试流程只在Linux上可行。我不是很确定它是否在其它Unixs系统上工作。macOS对此还不支持。Windows现在也不支持。
在我们开始前,需要确保核心转储的ulimit设置在合适的范围。它的缺省值是0,意味着最大的核心文件大小是0。我通常在我的开发机器上将它设置成unlimited。使用以下命令:
接下来,你需要在你的机器上安装 delve 。
下面我们使用的 main.go 文件。它注册了一个简单的请求处理函数(handler)然后启动了HTTP服务。
让我们编译并生产二进制文件。
现在让我们假设,这个服务器出了些问题,但是我们并不是很确定问题的根源。你可能已经在程序里加了很多辅助信息,但还是无法从这些调试信息中找出线索。通常在这种情况下,当前进程的快照会非常有用。我们可以用这个快照深入查看程序的当前状态。
有几个方式来获取核心文件。你可能已经熟悉了奔溃转储(crash dumps)。它们是在一个程序奔溃的时候写入磁盘的核心转储。Go语言在缺省设置下不会生产奔溃转储。但是当你把 GOTRACEBACK 环境变量设置成“crash”,你就可以用 Ctrl+backslash 才触发奔溃转储。如下图所示:
上面的操作会使程序终止,将堆栈跟踪(stack trace)打印出来,并把核心转储文件写入磁盘。
另外个方法可以从一个运行的程序获得核心转储而不需要终止相应的进程。 gcore 可以生产核心文件而无需使运行中的程序退出。
根据上面的操作,我们获得了转储而没有终止对应的进程。下一步就是把核心文件加载进delve并开始分析。
差不多就这些。delve的常用操作都可以使用。你可以backtrace,list,查看变量等等。有些功能不可用因为我们使用的核心转储是一个快照而不是正在运行的进程。但是程序执行路径和状态全部可以访问。
1、下载go的zip文件。并且一定要把文件解压到c:\go目录下。
2、配置windows的高级环境变量。包括:GOROOT、GOOS、GOBIN、GOARCH。并且在path变量里面把c:\go\bin加入。以便可以在命令行直接运行go命令。
举例:我的机器:
GOPATH= c:\go;c:\go\src;F:\workspace\goSample01;
GOBIN=c:\go\bin;F:\workspace\goSample01\bin;
其中,c:\go是go的安装路径;
F:\workspace\goSample01是我写的go语言项目的工程目录;
F:\workspace\goSample01\bin是go语言项目的工程目录下的可执行文件路径;
3、在完成环境变量配置后,打开一个命令行窗口,直接输入go,然后回车,看看是否出现go的帮助信息。如果出现,那么go的基本环境就OK了。
注意:这个基本环境不包含开发工具,也不能直接编译带C代码的go程序。
4、(可选)为了支持Import远程包,最好装个gomingw。下载地址:。如果下的是压缩包,请把它解压到C盘。例如,C:\gowin-env。里面有个Console.bat是以后使用go get的环境。举例:有个文件a.go,里面import(
"fmt"
"github.com/astaxie/beedb"
_ "github.com/ziutek/mymysql/godrv"
为了编译该a.go文件,需要启动Console.bat,然后在该命令行窗口,进入c:\go\src目录下,执行go getgithub.com/astaxie/beedb
Go get github.com/ziutek/mymysql/godrv .
Go会自动下载该远程包并编译和安装这些包。
配置goclipse(可选)
(如果不喜欢eclipse开发工具,请跳过这个配置。)
1、下载并安装goclipse插件。Goclipse是go语言for eclipse的插件,下载地址:
2、启动eclipse并创建go项目。然后写个最简单的helloworld.go文件,并运行。代码如下:
packagemainimport"fmt"func main(){ fmt.Printf("hello, world")}
配置gocode(可选)
如果不需要go语法辅助和eclipse里面的(按ALT+/)弹出go语言自动辅助功能,请跳过这个配置。
1、下载gocode的zip文件,解压后放在go的bin目录下。
2、下载并安装Git软件。并且在path里面配置git的执行路径。例如c:\git\bin
3、在命令行执行:go build .\gocode。如果一切正常,那么将会编译生成一个gocode.exe文件在go的bin目录下。如果编译失败,那么就转第4步。
4、如果第3步直接编译gocode源文件成功,那就直接到第5步。否则,就需要通过git下载gocode源文件,然后再编译。在命令行执行:go get -u github.com/nsf/gocode 。就会生成gocode.exe文件。
5、在goclipse插件里面指定gocode的路径。就可以在elcipse里面调用gocode来帮助写编码了。
从开发工具这块看,go语言还不够成熟,开发工具都还不完善,有待改进。
下载go-tour教程源代码(可选)
Google有个在线运行go语言的教程(),很不错。支持在web上直接运行大部分的go程序,想了解这个教程的源代码的朋友可以通过以下方式获取。如果没兴趣,可以跳过这个步骤。
1、下载安装Mercurial软件。
2、在命令行下输入:
hg clone
这个URL是我从google的go-tour源代码的一个clone。作为测试用的。如果把http改成https协议,下载就会失败。搞不懂。
编译带调用C代码的go文件(可选)
1、为了在windows下编译带C代码的go程序,你首先需要下载并安装MinGW或者Cygwin。
2、首选安装MinGW。在安装MinGW之后,记得要把MinGW安装目录\bin路径设置在path环境变量里面,以便能在dos窗口下直接调用gcc。
3、下载一个gowin-env。下载地址:gowin-env。下载后解压到某个目录下,例如:C:\gowin-env. 然后,编辑go-env.bat。配置相关的go参数。例如,我的配置是:
set GOARCH=386
set GOOS=windows
set GOROOT=c:\go
set GOBIN=%GOROOT%\bin
set GOPATH=%GOROOT%;F:\workspace\goSample01;
设置好go-env.bat后,就可以点击Console.bat来启动编译和运行窗口。
4、编写一个带C代码的go程序。例如,testc.go
5、编译
例如:
go build -compiler gccgo test_c.go
运行调用C代码的go文件(可选)
1、testc.go.
创建rand目录,然后在rand里面创建testc.go. 代码如下:
package rand
/*
//
#include stdio.h
*/
import "C"
func PrintHello() {
C.puts(C.CString("Hello, world\n"))
}
2、a.go
在rand下创建a.go.代码如下:
package rand
import "fmt"
func SayHello(name string){
fmt.Println(name)
}
3、test_import.go
在rand的上一级创建test_import.go。代码如下:
package main
import "./rand"
func main(){
rand.SayHello("tom")
rand.PrintHello()
}
4、运行test_import.go
go run test_import.go
在测试其它几个C代码的时候,发现windows版本的cgo还有些编译问题,同样的代码转移到苹果的XCODE下就没有问题。后来终于发现原因了,原来有些例子是unix平台下的,而在windows平台下,方法名和参数需要做调整。
例如:下面代码在windows下编译报一堆错误。
package rand
/*
#include stdlib.h
*/
import "C"
func Random() int {
return int(C.random())
}
func Seed(i int) {
C.srandom(C.uint(i))
}
这里需要把return int(C.random()) 修改为“return int(C.rand())”
C.srandom(C.uint(i))修改为“C.srand(C.uint(i))”编译就OK了。