Channel是什么

在Go语言中,Channel即指通道类型。有时也用它来直接指代可以传递某种类型的值的通道。

类型表示法

chan T

关键字chan代表了通道类型的关键字,T则代表了该通道类型的元素类型。

例如:type IntChan chan int 别名类型IntChan代表了元素类型为int的通道类型。我们可以直接声明一个chan int类型的变量:var IntChan chan int,在被初始化后,变量IntChan就可以被用来传递int类型的元素值了。

chan<- T

只能被用来发送值, <-表示发送操作符

<-chan T

接收通道值, <-表示接收操作符

值表示法

属性和基本操作

  • 基于通道的通讯是在多个Goroutine之间进行同步的重要手段。而针对通道的操作本身也是同步的。
  • 在同一时刻,仅有一个Goroutine能向一个通道发送元素值
  • 同时也仅有一个Goroutine能从它那里接收元素值。
  • 通道相当于一个FIFO先进先出的消息队列。
  • 通道中的元素值都具有原子性。它们是不可被分割的。通道中的每一个元素都只可能被某一个Goroutine接收。已被接收的元素值会立刻被从通道中删除。

初始化通道

make(chan int, 10)

~ 表达式初始化了一个通道类型的值。传递给make函数的第一个参数表明此值的具体类型是元素类型为int的通道类型,而第二个参数则指该值在同一时刻最多可以容纳10个元素值。

package main
import (
    "fmt"
)

type Person struct {
    Name string
    Age  uint8
    Address Addr
}

type Addr struct{
    city string
    district string
}

func main(){
    persionChan := make(chan Person,1)

    p1 := Person{"Harry",32,Addr{"Shanxi","Xian"}}
    fmt.Printf("P1 (1): %v\n",p1)

    persionChan <- p1

    p1.Address.district = "shijingshan"
    fmt.Printf("P2 (2): %v\n",p1)

    p1_copy := <-persionChan
    fmt.Printf("p1_copy: %v\n",p1_copy)
}

go test.go 运行结果

P1 (1): {Harry 32 {Shanxi Xian}}
P2 (2): {Harry 32 {Shanxi shijingshan}}
p1_copy: {Harry 32 {Shanxi Xian}}

通道中的元素值丝毫没有受到外界的影响。这说明了,在发送过程中进行的元素值属于完全复制。这也保证了我们使用通道传递的值的不变性。

关闭通道

close(strChan)

我们应该先明确一点:无论怎么样都不应该在接收端关闭通道。因为在那里我们无法判断发送端是否还会向该通道发送元素值。

package main

import (
    "fmt"
    "time"
)

func main(){
    ch := make(chan int, 5)
    sign := make(chan int, 2)

    go func() {
        for i :=0;i<5;i++ {
            ch <- i
            time.Sleep(1 * time.Second)
        }
        close(ch)
        fmt.Println("The channel is closed.")
        sign <- 0
    }()

    go func() {
        for {
            e, ok := <-ch
            fmt.Printf("%d (%v)\n", e,ok)
            if !ok {
                break
            }
            time.Sleep(2 * time.Second)
        }
        fmt.Println("Done.")
        sign <- 1
    }()
    <- sign
    <- sign
}

    运行结果:
    0 (true)
    1 (true)
    2 (true)
    The channel is closed.
    3 (true)
    4 (true)
    0 (false)
    Done.

运行时系统并没有在通道ch被关闭之后立即把false作为相应接收操作的第二个结果,而是等到接收端把已在通道中的所有元素值都接收到之后才这样做。这确保了在发送端关闭通道的安全性。

爬虫系统的要求

基本要求

  • 遍历链接,并获取网页
  • 网页解析,分析
  • 数据存储
  • 日志: 错误日志, 运行日志

分布式要求

  • 并行爬取: 效率高, 可以设定爬虫的数目
  • 任务管理: 任务队列, 出错了可以重新爬取
  • 代理: 考虑到爬取太多,可能会触发拦截,因此需要通过代理。
  • 策略: 可以灵活的设定策略, 爬取频率, 爬取数量, 使用不同的网管,使用不同的header

爬虫管理

  • 不同的网站,爬虫的规则是不一样的,因此爬虫可以自定义规则
  • 脚本: 有些网站可能需要登录认证。

循序渐进

v0.1 http请求

    baseUrl := "http://www.xxx.cn"
    req,_  := http.NewRequest("GET",baseUrl,nil)
    req.Header.Set("User-Agent", GetRandomUserAgent())
    client := http.DefaultClient
    res, e := client.Do(req)

    if e !=nil {
        fmt.Print(e)
        return
    }

    if res.StatusCode == 200 {
        body := res.Body
        defer body.Close()
        bodyByte,_ := ioutil.ReadAll(body)
        resStr := string(bodyByte)
        print(resStr)
    }

序言

看过很多方面的编码规范,可能每一家公司都有不同的规范,这份编码规范是写给我自己的,同时希望我们公司内部同事也能遵循这个规范来写Go代码。

如果你的代码没有办法找到下面的规范,那么就遵循标准库的规范,多阅读标准库的源码,标准库的代码可以说是我们写代码参考的标杆。
格式化规范

go默认已经有了gofmt工具,但是我们强烈建议使用goimport工具,这个在gofmt的基础上增加了自动删除和引入包.

go get golang.org/x/tools/cmd/goimports

不同的编辑器有不同的配置, sublime的配置教程:http://michaelwhatcott.com/gosublime-goimports/

LiteIDE默认已经支持了goimports,如果你的不支持请点击属性配置->golangfmt->勾选goimports

保存之前自动fmt你的代码。
行长约定

一行最长不超过80个字符,超过的请使用换行展示,尽量保持格式优雅。
go vet

vet工具可以帮我们静态分析我们的源码存在的各种问题,例如多余的代码,提前return的逻辑,struct的tag是否符合标准等。

go get golang.org/x/tools/cmd/vet

使用如下:

go vet .

package名字

保持package的名字和目录保持一致,尽量采取有意义的包名,简短,有意义,尽量和标准库不要冲突。
import 规范

import在多行的情况下,goimports会自动帮你格式化,但是我们这里还是规范一下import的一些规范,如果你在一个文件里面引入了一个package,还是建议采用如下格式:

import (
"fmt"
)

如果你的包引入了三种类型的包,标准库包,程序内部包,第三方包,建议采用如下方式进行组织你的包:

import (
"encoding/json"
"strings"

"myproject/models"
"myproject/controller"
"myproject/utils"

"github.com/astaxie/beego"
"github.com/go-sql-driver/mysql"

)

有顺序的引入包,不同的类型采用空格分离,第一种实标准库,第二是项目包,第三是第三方包。

在项目中不要使用相对路径引入包:

// 这是不好的导入
import “../net”

// 这是正确的做法
import “github.com/repo/proj/src/net”

变量申明

变量名采用驼峰标准,不要使用_来命名变量名,多个变量申明放在一起

var (
Found bool
count int
)

在函数外部申明必须使用var,不要采用:=,容易踩到变量的作用域的问题。
自定义类型的string循环问题

如果自定义的类型定义了String方法,那么在打印的时候会产生隐藏的一些bug

type MyInt int
func (m MyInt) String() string {
return fmt.Sprint(m) //BUG:死循环
}

func(m MyInt) String() string {
return fmt.Sprint(int(m)) //这是安全的,因为我们内部进行了类型转换
}

避免返回命名的参数

如果你的函数很短小,少于10行代码,那么可以使用,不然请直接使用类型,因为如果使用命名变量很
容易引起隐藏的bug

func Foo(a int, b int) (string, ok){

}

当然如果是有多个相同类型的参数返回,那么命名参数可能更清晰:

func (f *Foo) Location() (float64, float64, error)

下面的代码就更清晰了:

// Location returns f's latitude and longitude.
// Negative values mean south and west, respectively.
func (f *Foo) Location() (lat, long float64, err error)

错误处理

错误处理的原则就是不能丢弃任何有返回err的调用,不要采用_丢弃,必须全部处理。接收到错误,要么返回err,要么实在不行就panic,或者使用log记录下来
error 信息

error的信息不要采用大写字母,尽量保持你的错误简短,但是要足够表达你的错误的意思。
长句子打印或者调用,使用参数进行格式化分行

我们在调用fmt.Sprint或者log.Sprint之类的函数时,有时候会遇到很长的句子,我们需要在参数调用处进行多行分割:

下面是错误的方式:

log.Printf(“A long format string: %s %d %d %s”, myStringParameter, len(a),
expected.Size, defrobnicate(“Anotherlongstringparameter”,
expected.Growth.Nanoseconds() /1e6))

应该是如下的方式:

log.Printf(
“A long format string: %s %d %d %s”,
myStringParameter,
len(a),
expected.Size,
defrobnicate(
“Anotherlongstringparameter”,
expected.Growth.Nanoseconds()/1e6,
),

注意闭包的调用

在循环中调用函数或者goroutine方法,一定要采用显示的变量调用,不要再闭包函数里面调用循环的参数

fori:=0;i<limit;i++{
go func(){ DoSomething(i) }() //错误的做法
go func(i int){ DoSomething(i) }(i)//正确的做法
}

http://golang.org/doc/articles/race_detector.html#Race_on_loop_counter
在逻辑处理中禁用panic

在main包中只有当实在不可运行的情况采用panic,例如文件无法打开,数据库无法连接导致程序无法
正常运行,但是对于其他的package对外的接口不能有panic,只能在包内采用。

强烈建议在main包中使用log.Fatal来记录错误,这样就可以由log来结束程序。
注释规范

注释可以帮我们很好的完成文档的工作,写得好的注释可以方便我们以后的维护。详细的如何写注释可以
参考:http://golang.org/doc/effective_go.html#commentary
bug注释

针对代码中出现的bug,可以采用如下教程使用特殊的注释,在godocs可以做到注释高亮:

// BUG(astaxie):This divides by zero.
var i float = 1/0

http://blog.golang.org/2011/03/godoc­documenting­go­code.html
struct规范
struct申明和初始化格式采用多行:

定义如下:

type User struct{
Username string
Email string
}

初始化如下:

u := User{
Username: "astaxie",
Email: "[email protected]",
}

recieved是值类型还是指针类型

到底是采用值类型还是指针类型主要参考如下原则:

func(w Win) Tally(playerPlayer)int //w不会有任何改变
func(w *Win) Tally(playerPlayer)int //w会改变数据

更多的请参考:https://code.google.com/p/go-wiki/wiki/CodeReviewComments#Receiver_Type
带mutex的struct必须是指针receivers

如果你定义的struct中带有mutex,那么你的receivers必须是指针
参考资料

https://code.google.com/p/go-wiki/wiki/CodeReviewComments
http://golang.org/doc/effective_go.html

资源

官网

  • http://golang.org/
  • http://tour.golang.org
  • http://golang.org/doc/

博客

  • http://www.cnblogs.com/zitsing/tag/go/
  • http://www.mikespook.com/learning-go/

书籍

  • go语言编程
  • go web编程
  • go语言学习笔记
  • Effective Go
  • The Go Programming Language

Specification》、go标准库和github上众多开源库

go 学习难点(待整理)

  • http://blog.csdn.net/hittata/article/details/42387297

你想凭着一张现有图片找出它的原始图片,或者是凭着一张小的缩略图找出原始大图吗?

下面的十款搜索引擎可以帮你实现,以图找图,以图搜图,以图片搜索相似的图片。

一:http://tineye.com/

Tineye是典型的以图找图搜索引擎,输入本地硬盘上的图片或者输入图片网址,即可自动帮你搜索相似图片,搜索准确度相对来说还比较令人满意。

TinEye是加拿大Idée公司研发的相似图片搜索引擎,TinEye主要用途有:1、发现图片的来源与相关信息;2、研究追踪图片信息在互联网的传播;3、找到高分辨率版本的图片;4、找到有你照片的网页;5、看看这张图片有哪些不同版本。

二:http://shitu.baidu.com

百度正式上线了其最新的搜索功能——“识图”(shitu.baidu.com)。该功能是百度基于相似图片识别技术,让用户通过上传本地图片或者输入图片的URL地址之后,百度再根据图像特征进行分析,进而从互联网中搜索出与此相似的图片资源及信息内容。但需要注意的是,用户上传本地图片时,图片的文件要小于5M,格式可为JPG、JPEG、GIF、PNG、BMP等图片文件。

三:http://www.gazopa.com/

GazoPa搜索图片时,不依据关键词进行检索,而是通过图片自身的某些特征(例如色彩,形状等信 息)来进行搜索。GazoPa搜索方式有四种:

第一种是传统的通过关键词搜索图片,但在传统图片搜索领域GazoPa与google等搜索引擎无法竞争。

第二种是创新的通过图片搜索图片,但在此领域GazoPa无法与TinEye相竞争。TinEye很容易就能搜索出与原图最接近的一些结果,而GazoPa很多时候的搜索结果则完全无法与原图匹配。

第三种是通过手绘图片搜索图片,这种方式其实没太大用处。GazoPa虽然有这样那样的不足之处,但也算是一个很有独创性的搜索引擎。GazoPa目前还处在内测阶段,想要加入测试的可以在官网上留下你的邮箱地址,收到邀请后你就可以测试使用了。

第四种是通过视频缩略图搜索视频,GazoPa仅凭一张视频缩略图就可找到相关视频。只要有截图,就可以找到截图的视频!

四、http://similar-images.googlelabs.com/

Google实验室的图片搜索:输入一个关键词后,例如“lake”,返回的页面里面点击某个图片的下面的Similar images,运用Google 类似图片搜索功能引擎,即刻为你把类似的图片全部搜索出来,展示给用户以便查看。其准确率、相似率相对比较高。

五、http://www.picitup.com/

Picitup是一个刚开始公测的专业图片搜索引擎,功能非常强大,并支持中文关键字的搜索,是国内图片爱好者的不错选择。Picitup主要支持关键字的搜索,但在它的特色搜索项目——名人匹配搜索(Celebritymatchup)中,你可以通过上传本地照片来进行搜索,不过结果一般让人失望。Picitup可以通过在搜索结果页选择过滤方式来筛选图片,比如可以按颜色、头像(人脸)、风景、产品四种类别来过滤搜索结果。

Picitup最大特点是提供相似图片搜索,即通过关键字找到初始图片,点击初始图片下面的similar pictures按钮,即可搜索与该张图片类似的图片。其实质和Google实验室类似图片搜索是一样的。

六、http://www.tiltomo.com/

Tiltomo是由 Flickr 开发的一个搜索工具,主要用来维护Flickr 自己的图片数据库,其搜索算法主要是基于相似的主题风格或相似的色调和材质。

七、http://cn.bing.com/

Live.com允许你进行一次关键字搜索后再执行相似性的搜索。你可以为Live索引中的任意一张图片寻找相似的图片,但搜索结果看起来并不是很精确。

八、http://www.xcavator.net

Xcavator 和Live.com很相似,你需要先输入一个关键字,然后在搜索结果中挑选一张图片,在根据这张图片的特点来进行搜索。

九、http://www.incogna.com

Incogna的搜索速度非常快,主要是基于色彩和形状上的相似性。

十、http://www.terragalleria.com

Terragalleria主要基于视觉上的相似性,而不考虑图片的内容。

十一、http://labs.ideeinc.com/upload/
Byo image search是根据你上传的图片来搜索相似的图片,算法主要是基于色彩,也包括主题风格。