Go实例解析
Go语言包的加载顺序如图
可以通过如下实例详细了解
代码来源于《Go实战》
代码地址:https://github.com/goinaction/code
项目代码结构
程序架构
首先分析main文件
main.main.go
package main
import (
"log"
"os"
_ "github.com/goinaction/code/chapter2/sample/matchers" // 对包做初始化操作,但是不使用包里的标志符。调用包里所有的init函数
"github.com/goinaction/code/chapter2/sample/search"
)
// init is called prior to main.
func init() {
// Change the device for logging to stdout.
log.SetOutput(os.Stdout)
}
// main is the entry point for the program.
func main() {
// Perform the search for the specified term.
search.Run("president")
}
在执行main文件之前,会首先加载matchers、search包里的init函数和包级别的常量变量定义
加载matchers包的变量常量定义和init函数
type (
item struct {
XMLName xml.Name `xml:"item"`
PubDate string `xml:"pubDate"`
Title string `xml:"title"`
Description string `xml:"description"`
Link string `xml:"link"`
GUID string `xml:"guid"`
GeoRssPoint string `xml:"georss:point"`
}
image struct {
XMLName xml.Name `xml:"image"`
URL string `xml:"url"`
Title string `xml:"title"`
Link string `xml:"link"`
}
channel struct {
XMLName xml.Name `xml:"channel"`
Title string `xml:"title"`
Description string `xml:"description"`
Link string `xml:"link"`
PubDate string `xml:"pubDate"`
LastBuildDate string `xml:"lasteBuildDate"`
TTL string `xml:"ttl"`
Language string `xml:"language"`
ManagingEditor string `xml:"managingEditor"`
WebMaster string `xml:"webMaster"`
Image image `xml:"image"`
Item []item `xml:"item"`
}
rssDocument struct {
XMLName xml.Name `xml:"rss"`
Channel channel `xml:"channel"`
}
)
type rssMatcher struct{}
func init() {
var matcher rssMatcher
search.Register("rss", matcher)
}
加载search包的常量变量定义和init函数
const dataFile = "data/data.json"
type Feed struct {
Name string `json:"site"`
URI string `json:"link"`
Type string `json:"type"`
}
type Result struct {
Field string
Content string
}
type Matcher interface {
Search(feed *Feed, searchTerm string) ([]*Result, error)
}
var matchers = make(map[string]Matcher)
type defaultMatcher struct{}
func init() {
var matcher defaultMatcher
Register("default", matcher)
}
在回到main函数,首先执行search.Run()的函数,
func Run(searchTerm string) {
feeds, err := RetrieveFeeds()
if err != nil {
log.Fatal(err)
}
results := make(chan *Result)
//添加线程等待
var waitGroup sync.WaitGroup
waitGroup.Add(len(feeds))
for _, feed := range feeds {
log.Printf("%v", *feed)
matcher, exists := matchers[feed.Type]
if !exists {
matcher = matchers["default"]
}
//起协程是执行匹配
go func(matcher Matcher, feed *Feed) {
Match(matcher, feed, searchTerm, results)
waitGroup.Done()
}(matcher, feed)
}
go func() {
waitGroup.Wait()
close(results)
}()
Display(results)
}
在Run函数中,执行RetrieveFeeds()函数
func RetrieveFeeds() ([]*Feed, error) {
//打开配置文件
file, err := os.Open(dataFile)
if err != nil {
return nil, err
}
defer file.Close()
var feeds []*Feed
//解析json格式到Feed结构体
err = json.NewDecoder(file).Decode(&feeds)
return feeds, err
}
执行匹配
func Match(matcher Matcher, feed *Feed, searchTerm string, results chan<- *Result) {
searchResults, err := matcher.Search(feed, searchTerm)
if err != nil {
log.Println(err)
return
}
//将匹配到的结果写入chan
for _, result := range searchResults {
results <- result
}
}
执行搜索
func (m rssMatcher) Search(feed *search.Feed, searchTerm string) ([]*search.Result, error) {
var results []*search.Result
log.Printf("search Feed Type[%s] Site[%s] For URI[%s]\n", feed.Type, feed.Name, feed.URI)
document, err := m.retrieve(feed)
if err != nil {
return nil, err
}
for _, channelItem := range document.Channel.Item {
matched, err := regexp.MatchString(searchTerm, channelItem.Title)
if err != nil {
return nil, err
}
if matched {
results = append(results, &search.Result{
Field: "Title",
Content: channelItem.Title,
})
}
matched, err = regexp.MatchString(searchTerm, channelItem.Description)
if err != nil {
return nil, err
}
if matched {
results = append(results, &search.Result{
Field: "Description",
Content: channelItem.Description,
})
}
}
return results, nil
}
func (m rssMatcher) retrieve(feed *search.Feed) (*rssDocument, error) {
if feed.URI == "" {
return nil, errors.New("NO rss feed uri provided")
}
//http获取uri返回
resp, err := http.Get(feed.URI)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, fmt.Errorf("HTTP Response Error %d\n", resp.StatusCode)
}
//将结果解析到xml格式的struct
var document rssDocument
err = xml.NewDecoder(resp.Body).Decode(&document)
return &document, err
}
最后展示结构
func Display(results chan *Result) {
//从chan中读取,会阻塞直到chan close
for result := range results {
log.Printf("%s:\n%s\n\n", result.Field, result.Content)
}
}