132015
 

微信支付获取 prepay id 偶尔失败问题总结。

微信支付会要求先从微信服务器获取 prepay id (https://api.mch.weixin.qq.com/pay/unifiedorder)。我们开发完成后(语言是PHP,使用微信的支付SDK,请求时使用curl),在测试环境的机器上,基本没有发现请求失败的情况,上线后,却发现经常出现错误,概率1/5甚至更高。开始没有深究原因,采用重试的方式,不过发现,只要失败了,重试也会失败。

记录下 curl 的错误是:errno:35, error: SSL Connect Error。

网上查相关资料,没有找到解决方案。联系微信技术支持,他们没有任何建议,觉得是我们的问题,让我们自己查。

在我们服务器上通过 tcpdump 抓包:tcpdump -i eth1 ip host 140.207.69.102 -w wxpay.cap,对比成功和失败的包(使用wireshark分析):

成功的数据包:

失败的数据包:

可以看到,失败的时候,TCP 3次握手后马上进行4次挥手操作 没有任何内容交互,而且 close 是客户端(我们这边)发起的。

推论:与remote服务端应该无关,还没开始https内容部分的交换,应该是本地在进行某些工作的时候发现异常,close了连接(但程序如果让我写会考虑先本地工作结束后再connect,不清楚为什么先connect然后再获取本地资源)。

可以通过 strace curl -X POST https://api.mch.weixin.qq.com/pay/unifiedorder 查看整个系统调用过程,是先 connect,然后从 系统本地etc目录下加载一些CA相关文件。

也可以通过 curl -v -X POST https://api.mch.weixin.qq.com/pay/unifiedorder 查看相关信息。对于 PHP 中,可以设置:curl_setopt($ch, CURLOPT_VERBOSE, true)

失败时是类似这样的输出:

* About to connect() to api.mch.weixin.qq.com port 443 (#0)
* Trying 140.207.69.102… connected
* Connected to api.mch.weixin.qq.com (140.207.69.102) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* NSS error -5990
* Closing connection #0
* SSL connect error
curl: (35) SSL connect error

成功时输出如下(省略了response):

* About to connect() to api.mch.weixin.qq.com port 443 (#0)
* Trying 140.207.69.102… connected
* Connected to api.mch.weixin.qq.com (140.207.69.102) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* NSS: client certificate not found (nickname not specified)
* SSL connection using TLS_DHE_RSA_WITH_AES_256_CBC_SHA
* Server certificate:
* subject: CN=payapp.weixin.qq.com,OU=R&D,O=Tencent Technology (Shenzhen) Company Limited,L=shenzhen,ST=guangdong,C=CN
* start date: 4月 28 00:00:00 2015 GMT
* expire date: 4月 27 23:59:59 2016 GMT
* common name: payapp.weixin.qq.com
* issuer: CN=GeoTrust SSL CA – G2,O=GeoTrust Inc.,C=US
> POST /pay/unifiedorder HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.16.2.3 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: api.mch.weixin.qq.com
> Accept: */*

在网上找到了一个帖子: http://serverfault.com/questions/606135/curl-35-ssl-connect-error, 问题和我们遇到的一样,虽然操作系统版本和NSS版本不一样。按上面的提示,升级 nss(Mozilla Network Security Services 网络安全服务):yum update nss, 然后重启 php,问题解决。

总结:
1、从现象:有些机器有问题,有些没有问题;有些概率大,有些概率小;可大体上推断,跟机器有关系,可能机器环境不一样导致的;
2、抓包分析原因,进一步确认不是服务端(微信支付)的问题;
3、根据错误描述在网络上寻求帮助;
4、学习 curl 相关选项的使用;strace 的使用;

302013
 

目前开始写一本书 《Golang标准库》,直接可以在Github上看。https://github.com/polaris1119/The-Golang-Standard-Library-by-Example。

2016年6月:最近开始更新了……

282013
 

期待已久的Go1.1终于快发布了。官方已经放出了Go1.1的改动介绍。想看详细信息的可以直接查看原文:Introduction to Go 1.1

这里就主要改动做下介绍

一、语言的改动

1、整数除以0问题

Go1.0x中,整数除以常量0会抛出运行时 panic, Go1.1改成了编译不通过,即 编译时 错误,这样可以早发现问题。主意,是常量0,而不是变量。

2、方法可以作为值

我们知道,在Go中func是一等公民,可以到处使用,比如作为函数参数、赋值给变量。但是方法(Method)却不行,也就是带有receiver的函数不是一等公民。

Go1.1将方法提升为一等公民了。这样,方法可以作为函数参数、赋值给变量等。

3、一直被视为bug的必须”return”问题

在Go1.0x中,如下代码编译不通过:

if true {
     return true
} else {
     return false
}

当然,这样的代码并不好。但有些时候多余的return真的是没有必要。这个问题很多人不可忍。也因为各种给官方提意见,以及一些确实没必要的地方,比如死循环、switch等,导致很多地方会来一句:panic(“unreachable”)。于是,Go1.1允许上面代码的存在。

二、编译器实现和工具链的改动

1、命令行flag解析

在gc工具链中,Go1.1采用了flag包一样的规则解析命令行参数。这使得Unix风格的参数,如go tool 6c -Fw -Dfoo现在必须改为:go tool 6 -F -w -D foo

2、int/uint类型在64-bit平台实现为64bits

Go1.0x中,int/uint在所有平台都是32bits。Go1.1中改为在64-bit平台是64bits,当然32-bit的平台还是32bits。

3、go命令的改动

1)编译、测试或运行go代码,出错时会有更详细的错误信息。比如新手经常遇到的包找不到问题。
2)go get命令不允许使用GOROOT作为目标目录,即必须设置GOPATH且GOPATH不能和GOROOT指向同一个目录。

4、go test命令的改动

当启用profiling的时候,Go1.1不再默认删除go test产生的二进制文件,这样更利于分析profile文件。如:

go test -cpuprofile cpuprof.out mypackage

mypackage.test将被留在运行go test所在的目录。

go test命令还能够生成profiling信息反映goroutines在哪个地方堵塞了。这些信息会保存在一个bloking profile文件中,通过go test -blockprofile 指定。

5、go fix命令

该命令不再用来修复Go1之前的代码(将其升级到Go1 APIs),对于Go1之前的代码,应该先用Go1.0的工具链来修复。也就是说,Go1.1中,该命令没法将Go1之前的代码fix成Go1

三、性能的提升

Go1.1的性能提升一般在30%-40%,主要的改动如下:

1、大部分情况下,gc编译器可以生产更好的代码,尤其是32-bit Intel架构下的浮点型

2、gc编译器做了更多内联工作,包括一些运行时的操作,如append和interface转换

3、重新实现了map数据类型,很大程度减少map的内存占用和CPU时间

4、支持并行GC(garbage collector),缩短GC “Stop the World”的时间

5、GC(garbage collector)更精确,能够花更少的CPU时间但是能够明显的释放heap内存,尤其是在32-bit架构

6、将net poller 集成到了 runtime(network包和runtime紧密耦合),这样,网络操作时上下文切换会更少

四、标准库的改动

1、bufio包

增加了Scanner类型

2、net包

严格限制了网络名,如ResolveTCPAddr只能是”tcp”,”tcp4″和”tcp6″(Go1.0x中是可以任意字符串的);

ListenUnixgram返回类型由原来的UDPConn改为UnixConn,这样可以使用ReadFrom和WriteTo方法;

IPAddr、TCPAddr和UDPAddr增加了一个新字段:Zone。这样,原来类似这样的代码(net.TCPAddr{ip, port})得修改为:net.TCPAddr{IP: ip, Port: port})

3、reflect包

增加了Select函数(支持select语言)http://tip.golang.org/pkg/reflect/#Select

增加类型断言通过Value.Convert或Type.ConvertibleTo方法

新函数MakeFunc创建一个函数包装器,使得调用函数更简单(以存在的Values为参数),它会做Go中的标准类型转换,比如传递一个int类型到interface{}(当然是反射的方式)

增加了ChanOf、MapOf和SliceOf,方便从已存在的类型创建新的类型,如仅仅通过T创建[]T

4、time包

增加了一些新方法

5、标准库的其他一些小改动不一一细说了,用到时候查阅一下文档吧

五、升级现有代码

如果你的代码是Go1.0x写的,需要升级到Go1.1,这是很容易的事情。具体可以根据实际情况看看Introduction to Go 1.1中各个点的updating

另附 星星 翻译的完整版 Go 1.1 介绍

Go1.1要来了,期待一下吧。

十一 292012
 

Go语言推出已经有几年时间了,正式版推出也快一年了。Go语言社区还算活跃。

本人从2012年6月份开始接触Go,并在两、三个月后有幸在工作中使用Go语言进行软件开发。在这期间,学习了Go的一些知识,同时在Go语言QQ讨论群组和广大Go语言爱好者交流、探讨问题。

Go语言发展到现在,市面上已经有不少相关的书籍、资料。然而,深入Go语言方面的资料却很少。当然,肯定有大牛们对Go语言的标准库、原理很熟悉,只是没有将这些与大家分享,期待这些大牛将知识分享出来,为Go语言的推广做点贡献。

本网站的目的,一方面将自己所学的Go语言知识与大家分享;另一方面也希望有更多的大牛能够分享他们所学的Go语言知识。另外,网站中会转载一些好文章。大家可以一起参与这个网站,让这里成为学习Go语言的园地!

目前该网站使用WordPress搭建,后期会推出Go语言版本,有兴趣的可以一起参与哦☺