关于Go语言的介绍
优势
智能的编译器,并简化了解决依赖的算法,最终提供了更快的编译速度
支持并发:goroutine,通道
类型系统:组合代替继承,接口对行为建模
内存管理
Go Playground
http:
Go程序开发
项目结构
- sample
- data
data.json
- matchers
rss.go
- search
default.go
feed.go
match.go
search.go
main.go
main.go
package main
import (
"log"
"os"
_ "github.com/goinaction/code/chapter2/sample/matchers"
"github.com/goinaction/code/chapter2/sample/search"
)
func init() {
log.SetOutput(os.Stdout)
}
func main() {
search.Run("president")
}
search.go
package search
import (
"log"
"sync"
)
var matchers = make(map[string]Matcher)
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 {
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)
}
func Register(feedType string, matcher Matcher) {
if _, exists := matchers[feedType]; exists {
log.Fatalln(feedType, "Matcher already registered")
}
log.Println("Register", feedType, "matcher")
matchers[feedType] = matcher
}
feed.go
package search
import (
"encoding/json"
"os"
)
const dataFile = "data/data.json"
type Feed struct {
Name string `json:"site"`
URI string `json:"link"`
Type string `json:"type"`
}
func RetrieveFeeds() ([]*Feed, error) {
file, err := os.Open(dataFile)
if err != nil {
return nil, err
}
defer file.Close()
var feeds []*Feed
err = json.NewDecoder(file).Decode(&feeds)
return feeds, err
}
data.json
[ { "site" : "npr", "link" : "http://www.npr.org/rss/rss.php?id=1001", "type" : "rss" }, { "site" : "cnn", "link" : "http://rss.cnn.com/rss/cnn_world.rss", "type" : "rss" }, {"site" : "foxnews", "link" : "http://feeds.foxnews.com/foxnews/world?format=xml", "type" : "rss" }, { "site" : "nbcnews", "link" : "http://feeds.nbcnews.com/feeds/topstories", "type" : "rss" } ]
match.go
package search
import (
"log"
)
type Result struct {
Field string
Content string
}
type Matcher interface {
Search(feed *Feed, searchTerm string) ([]*Result, error)
}
func Match(matcher Matcher, feed *Feed, searchTerm string, results chan<- *Result) {
searchResults, err := matcher.Search(feed, searchTerm)
if err != nil {
log.Println(err)
return
}
for _, result := range searchResults {
results <- result
}
}
func Display(results chan *Result) {
for result := range results {
fmt.Printf("%s:\n%s\n\n", result.Field, result.Content)
}
}
default.go
package search
type defaultMatcher struct{}
func init() {
var matcher defaultMatcher
Register("default", matcher)
}
func (m defaultMatcher) Search(feed *Feed, searchTerm string) ([]*Result, error) {
return nil, nil
}
rss.go
package matchers
import (
"encoding/xml"
"errors"
"fmt"
"log"
"net/http"
"regexp"
"github.com/goinaction/code/chapter2/sample/search"
)
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:"lastBuildDate"`
TTL string `xml:"ttl"` 46 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)
}
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")
}
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)
}
var document rssDocument
err = xml.NewDecoder(resp.Body).Decode(&document)
return &document, err
}
rss示例
<rss xmlns:npr="http://www.npr.org/rss/" xmlns:nprml="http://api"
<channel>
<title>News</title>
<link>...</link>
<description>...</description>
<language>en</language>
<copyright>Copyright 2014 NPR - For Personal Use
<image>...</image>
<item>
<title> Putin Says He'll Respect Ukraine Vote But U.S.
</title>
<description> The White House and State Department have called on the
</description>
接口类型的值调用方法
使用指针作为接收者声 明的方法,只能在接口类型的值是一个指针的时候被调用
使用值作为 接收者声明的方法,在接口类型的值为值或者指针时,都可以被调用。
全部流程