利用golang并发下载股票数据(一)
ijibu · · 6729 次点击 · · 开始浏览先贴上代码
//批量获取雅虎股票数据。
package main
import (
"bufio"
"fmt"
"io"
"net/http"
"net/url"
"os"
"runtime"
"strconv"
"strings"
)
const (
UA = "Golang Downloader from Ijibu.com"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) //设置cpu的核的数量,从而实现高并发
c := make(chan bool)
fh, ferr := os.Open("./shang.ini") //上证里面的所有股票代码,每一行就是一个股票代码。
if ferr != nil {
return
}
defer fh.Close()
inputread := bufio.NewReader(fh)
for i := 0; i < 1162; i++ {
input, _ := inputread.ReadString('\n')
go getShangTickerTables(c, i, strings.TrimSpace(input))
}
<-c
fmt.Println("main ok")
}
func getShangTickerTables(c chan bool, n int, code string) {
fileName := "./data/sh/" + code + ".csv"
f, err := os.OpenFile(fileName, os.O_CREATE, 0666) //其实这里的 O_RDWR应该是 O_RDWR|O_CREATE,也就是文件不存在的情况下就建一个空文件,但是因为windows下还有BUG,如果使用这个O_CREATE,就会直接清空文件,所以这里就不用了这个标志,你自己事先建立好文件。
if err != nil {
panic(err)
}
stat, err := f.Stat() //获取文件状态
if err != nil {
panic(err)
}
defer f.Close()
urls := "http://table.finance.yahoo.com/table.csv?s=" + code + ".ss"
var req http.Request
req.Method = "GET"
req.Close = true
req.URL, err = url.Parse(urls)
if err != nil {
panic(err)
}
header := http.Header{}
header.Set("Range", "bytes="+strconv.Itoa(int(stat.Size()))+"-")
header.Set("User-Agent", UA)
req.Header = header
resp, err := http.DefaultClient.Do(&req)
if err != nil {
panic(err)
}
io.Copy(f, resp.Body)
if n == 1161 {
c <- true
}
defer resp.Body.Close()
}
上面的代码存在2个严重的问题
1.不管在windows还是在linux下执行都会出错。
以linux为例讲,最开始是错误如下:
runtime/cgo: pthread_create failed: Resource temporarily unavailable SIGABRT: abort PC=0xca2424 goroutine 1 [chan receive]: main.main() /root/go/src/ijibu/gettrackers/gettrackers.go:35 +0x13d goroutine 2 [syscall]: goroutine 4 [select]: net/http.(*Transport).getConn(0x18475120, 0x1850b8a0, 0x1850b8a0, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/transport.go:407 +0x1ef net/http.(*Transport).RoundTrip(0x18475120, 0x18507e00, 0x5, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/transport.go:181 +0x260 net/http.send(0x18507e00, 0x184629c0, 0x18475120, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/client.go:166 +0x2b8 net/http.(*Client).send(0x82e2240, 0x18507e00, 0x34, 0x18512058, 0xb730ef40, ...) /usr/local/go/src/pkg/net/http/client.go:100 +0xa5 net/http.(*Client).doFollowingRedirects(0x82e2240, 0x18507e00, 0x821244c, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/client.go:282 +0x4d0 net/http.(*Client).Do(0x82e2240, 0x18507e00, 0xa, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/client.go:129 +0x7b main.getShangTickerTables(0x18466270, 0x0, 0x18400418, 0x6) /root/go/src/ijibu/gettrackers/gettrackers.go:64 +0x2a7 created by main.main /root/go/src/ijibu/gettrackers/gettrackers.go:32 +0x117 goroutine 5 [select]: net/http.(*Transport).getConn(0x18475120, 0x1850b220, 0x1850b220, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/transport.go:407 +0x1ef net/http.(*Transport).RoundTrip(0x18475120, 0x18c43150, 0x5, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/transport.go:181 +0x260 net/http.send(0x18c43150, 0x184629c0, 0x18475120, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/client.go:166 +0x2b8 net/http.(*Client).send(0x82e2240, 0x18c43150, 0x34, 0x18ae6ec8, 0xb730df40, ...) /usr/local/go/src/pkg/net/http/client.go:100 +0xa5 net/http.(*Client).doFollowingRedirects(0x82e2240, 0x18c43150, 0x821244c, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/client.go:282 +0x4d0 net/http.(*Client).Do(0x82e2240, 0x18c43150, 0xa, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/client.go:129 +0x7b main.getShangTickerTables(0x18466270, 0x1, 0x18400428, 0x6) /root/go/src/ijibu/gettrackers/gettrackers.go:64 +0x2a7 created by main.main /root/go/src/ijibu/gettrackers/gettrackers.go:32 +0x117
runtime/cgo: pthread_create failed: Resource temporarily unavailable
google了一下是DNS查询的问题,vi /etc/hosts,加入
67.195.146.181table.finance.yahoo.com
即可解决上面的问题
2.根本没有按着预期请求完所有的数据
即,虽然我循环了1161次,预期是调用查询接口1162次,然后写入数据,但是并没有执行完1162次,实际只执行了100多次,因为其他文件都是为空。
鄙人分析,是goroutine的运行机制有关。参考《Go语言编程》前言《并发与分布式》部分,原话如下:"Go语言在语言级别支持协程,叫goroutine。Go语言标准库提供的所有系统调用(syscall)操作,当然也包括所有的同步IO操作,都会出让CPU给其它的goroutine",由此可见外层循环完了1162次,产生了1162个goroutine,这些goroutine有go语言的引擎负责自己调度执行,假设第1162个goroutine被较早的调度,此时channel就会被置为true,此时main就执行完了。如果是这种情况,那么
os.OpenFile(fileName, os.O_CREATE, 0666)
可能就执行不了1162次,那么就不能创建1162个文件。但是运行显示的确产生了1162个文件。说明goroutine的调度还是有顺序的。但是在执行每个goroutine(即getShangTickerTables函数)时,由于有网络操作,所以此时会出让CPU给其它的goroutine。由于网络IO比较费时,所以才会出现上面的情况,每个goroutine都执行了一部分,然后由于网络IO被切换,channel写入true,实际上每个
有疑问加站长微信联系(非本文作者)
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
关注微信- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码` - 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传
收入到我管理的专栏 新建专栏
先贴上代码
//批量获取雅虎股票数据。
package main
import (
"bufio"
"fmt"
"io"
"net/http"
"net/url"
"os"
"runtime"
"strconv"
"strings"
)
const (
UA = "Golang Downloader from Ijibu.com"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) //设置cpu的核的数量,从而实现高并发
c := make(chan bool)
fh, ferr := os.Open("./shang.ini") //上证里面的所有股票代码,每一行就是一个股票代码。
if ferr != nil {
return
}
defer fh.Close()
inputread := bufio.NewReader(fh)
for i := 0; i < 1162; i++ {
input, _ := inputread.ReadString('\n')
go getShangTickerTables(c, i, strings.TrimSpace(input))
}
<-c
fmt.Println("main ok")
}
func getShangTickerTables(c chan bool, n int, code string) {
fileName := "./data/sh/" + code + ".csv"
f, err := os.OpenFile(fileName, os.O_CREATE, 0666) //其实这里的 O_RDWR应该是 O_RDWR|O_CREATE,也就是文件不存在的情况下就建一个空文件,但是因为windows下还有BUG,如果使用这个O_CREATE,就会直接清空文件,所以这里就不用了这个标志,你自己事先建立好文件。
if err != nil {
panic(err)
}
stat, err := f.Stat() //获取文件状态
if err != nil {
panic(err)
}
defer f.Close()
urls := "http://table.finance.yahoo.com/table.csv?s=" + code + ".ss"
var req http.Request
req.Method = "GET"
req.Close = true
req.URL, err = url.Parse(urls)
if err != nil {
panic(err)
}
header := http.Header{}
header.Set("Range", "bytes="+strconv.Itoa(int(stat.Size()))+"-")
header.Set("User-Agent", UA)
req.Header = header
resp, err := http.DefaultClient.Do(&req)
if err != nil {
panic(err)
}
io.Copy(f, resp.Body)
if n == 1161 {
c <- true
}
defer resp.Body.Close()
}
上面的代码存在2个严重的问题
1.不管在windows还是在linux下执行都会出错。
以linux为例讲,最开始是错误如下:
runtime/cgo: pthread_create failed: Resource temporarily unavailable SIGABRT: abort PC=0xca2424 goroutine 1 [chan receive]: main.main() /root/go/src/ijibu/gettrackers/gettrackers.go:35 +0x13d goroutine 2 [syscall]: goroutine 4 [select]: net/http.(*Transport).getConn(0x18475120, 0x1850b8a0, 0x1850b8a0, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/transport.go:407 +0x1ef net/http.(*Transport).RoundTrip(0x18475120, 0x18507e00, 0x5, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/transport.go:181 +0x260 net/http.send(0x18507e00, 0x184629c0, 0x18475120, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/client.go:166 +0x2b8 net/http.(*Client).send(0x82e2240, 0x18507e00, 0x34, 0x18512058, 0xb730ef40, ...) /usr/local/go/src/pkg/net/http/client.go:100 +0xa5 net/http.(*Client).doFollowingRedirects(0x82e2240, 0x18507e00, 0x821244c, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/client.go:282 +0x4d0 net/http.(*Client).Do(0x82e2240, 0x18507e00, 0xa, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/client.go:129 +0x7b main.getShangTickerTables(0x18466270, 0x0, 0x18400418, 0x6) /root/go/src/ijibu/gettrackers/gettrackers.go:64 +0x2a7 created by main.main /root/go/src/ijibu/gettrackers/gettrackers.go:32 +0x117 goroutine 5 [select]: net/http.(*Transport).getConn(0x18475120, 0x1850b220, 0x1850b220, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/transport.go:407 +0x1ef net/http.(*Transport).RoundTrip(0x18475120, 0x18c43150, 0x5, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/transport.go:181 +0x260 net/http.send(0x18c43150, 0x184629c0, 0x18475120, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/client.go:166 +0x2b8 net/http.(*Client).send(0x82e2240, 0x18c43150, 0x34, 0x18ae6ec8, 0xb730df40, ...) /usr/local/go/src/pkg/net/http/client.go:100 +0xa5 net/http.(*Client).doFollowingRedirects(0x82e2240, 0x18c43150, 0x821244c, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/client.go:282 +0x4d0 net/http.(*Client).Do(0x82e2240, 0x18c43150, 0xa, 0x0, 0x0, ...) /usr/local/go/src/pkg/net/http/client.go:129 +0x7b main.getShangTickerTables(0x18466270, 0x1, 0x18400428, 0x6) /root/go/src/ijibu/gettrackers/gettrackers.go:64 +0x2a7 created by main.main /root/go/src/ijibu/gettrackers/gettrackers.go:32 +0x117
runtime/cgo: pthread_create failed: Resource temporarily unavailable
google了一下是DNS查询的问题,vi /etc/hosts,加入
67.195.146.181table.finance.yahoo.com
即可解决上面的问题
2.根本没有按着预期请求完所有的数据
即,虽然我循环了1161次,预期是调用查询接口1162次,然后写入数据,但是并没有执行完1162次,实际只执行了100多次,因为其他文件都是为空。
鄙人分析,是goroutine的运行机制有关。参考《Go语言编程》前言《并发与分布式》部分,原话如下:"Go语言在语言级别支持协程,叫goroutine。Go语言标准库提供的所有系统调用(syscall)操作,当然也包括所有的同步IO操作,都会出让CPU给其它的goroutine",由此可见外层循环完了1162次,产生了1162个goroutine,这些goroutine有go语言的引擎负责自己调度执行,假设第1162个goroutine被较早的调度,此时channel就会被置为true,此时main就执行完了。如果是这种情况,那么
os.OpenFile(fileName, os.O_CREATE, 0666)
可能就执行不了1162次,那么就不能创建1162个文件。但是运行显示的确产生了1162个文件。说明goroutine的调度还是有顺序的。但是在执行每个goroutine(即getShangTickerTables函数)时,由于有网络操作,所以此时会出让CPU给其它的goroutine。由于网络IO比较费时,所以才会出现上面的情况,每个goroutine都执行了一部分,然后由于网络IO被切换,channel写入true,实际上每个