亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Golang > 正文

五步讓你成為GO語言高手

2020-04-01 19:14:55
字體:
來源:轉載
供稿:網友
本文給大家介紹的這里是GO程序員的五個進化階段,從最開始的菜逼到最終的布道者,附上各種示例,一步步走向大神之路,推薦給小伙伴們,有需要的朋友可以參考下
 

Francesc (@francesc) 是 Go 核心團隊的一員, 是提倡 Google Cloud 平臺的開發者. 他是一個編程語言的愛好者, Google的技術指導大師, Go tour的創造者之一. 這個討論的靈感來自于另一個 Raquel Vélez 在 JSConf. Slides 的討論,這個討論已經發到了這里.

Sourcegraph 是下一代編程協作工具, 用于搜索, 探索, 和審查代碼. 我們參加GopherCon India 來分享我們是怎樣使用 Go 并學習別人是怎樣使用它的, 對配合liveblog的這次討論我們深感榮幸.

作為Go團隊的開發者之一,Francesc可能比世界上其他人接觸到的Go語言程序員都要多。正因為有了這樣的有利條件,他把Go語言的學習過程劃分為5個階段。

這些階段對于其他語言的學習也是成立的。理解自己處于哪個階段,可以幫助你找到提高自己的最有效的方法,也可以避免每個階段學習過程中的常見陷阱。

編者按:這篇文章對于每一個學習階段都給出了交互式的代碼片段。點擊函數名你就可以跳到具體的函數定義,方便進行深入的研究。請看下文。

這里是GO程序員的五個進化階段:

第一個階段(菜逼): 剛剛學習了這門語言。 已經通過一些教程或者培訓班了解基本的語法,可以寫短的代碼片段。

第二個階段 (探索者): 可以寫一個完整的程序,但不懂一些更高級的語言特征,比如“channels”。還沒有使用GO寫一個大項目。

第三個階段(大手): 你能熟練的使用Go, 能夠用GO去解決,生產環境中一個具體和完整的問題。已經形成了一套自己的慣用法和常用代碼庫。在你的編碼方案中Go是一個非常好用的工具。

第四階段 (大神): 絕逼清楚Go語言的設計選擇和背后的動機。能理解的簡潔和可組合性哲學。

布道師: 積極地與他人分享關于Go語言知識和你對Go語言的理解。在各種合適的場所發出自己的聲音, 參與郵件列表、建立QQ群、做專題報告。成為一個布道者不見得是一個完全獨立的階段,這個角色可以在上述的任何一個階段中。

第一階段: 菜逼

菜鳥在這個階段使用Go去創建一些小項目或者玩具項目。他們應該會利用到Go tour, Go playground, Go文檔, 和郵件列表(golang-nuts).

func main() {    fmt.Println(stringutil.Reverse("!selpmaxe oG ,olleH"))}

這是Go語言寫的簡單例子,這個代碼段來自golang/example代碼庫里面的 hello.go 。 點擊就可以查看完整代碼擼。

一項重要的技能,新人應該試著學習如何正確提問。很多新人在郵件列表里面這樣說“嘿,這報錯了”,這并沒有提供足夠的信息,讓別人能理解并幫助他們解決問題。別人看到的是一個粘貼了幾百行的代碼的帖子,并沒有花費精力來重點說明所遇到的問題。

所以, 應該盡量避免直接粘貼代碼到論壇。而應該使用可以編輯并且可以在瀏覽器中直接運行的Go playground的“分享”按鈕鏈接到代碼片段。

Phase 2: the explorer

探索者已經可以使用Go寫一些小的軟件,但有時仍然會有些迷茫。他們可能不完全明白怎么使用Go的高級特性,比如通道。雖然他們還有很多東西要學習,但已掌握的足夠做一些有用的事情了!他們開始對Go的潛能有感覺了,并對它們能使用Go創建的東西感到興奮。

五步讓你成為GO語言高手

在探索階段通常會經歷兩個步驟。第一,膨脹的預期達到頂點,你覺得可以用Go做所有的事情,但還并不能明白或領悟到Go的真諦。你大概會用所熟悉的語言的模式和慣用語來寫Go代碼,但對于什么是地道的Go,還沒有比較強烈的感覺。你開始嘗試著手干這樣的事情--“遷移架構X,從Y語言到Go語言”。

到達預期膨脹的頂點之后,你會遇到理想幻滅的低谷。你開始想念語言Y的特性X,此時你還沒有完全的掌握地道的Go。你還在用其他編程語言的風格來寫Go語言的程序,你甚至開始覺得沮喪。你可能在大量使用reflect和unsafe這兩個包,但這不是地道的Go。地道的Go不會使用那些魔法一樣的東西。

這個探索階段產生的項目的一個很好的例子就是Martini Web框架。Martini是一個Go語言的早期Web框架,它從Ruby的Web框架當中吸收了很多思想(比如依賴注入)。最初,這個框架在社區中引起了強烈的反響,但是它逐漸在性能和可調試性上受到了一些批評。Martini框架的作者Jeremy Saenz積極響應這些來自Go社區的反饋,寫了一個更加符合Go語言規范的庫Negroni

func (m *Martini) RunOnAddr(addr string) {    // TODO: Should probably be implemented using a new instance of http.Server in place of    // calling http.ListenAndServer directly, so that it could be stored in the martini struct for later use.    // This would also allow to improve testing when a custom host and port are passed.     logger := m.Injector.Get(reflect.TypeOf(m.logger)).Interface().(*log.Logger)    logger.Printf("listening on %s (%s)/n", addr, Env)    logger.Fatalln(http.ListenAndServe(addr, m))}

來自Martini框架的交互式代碼片段,它是不地道的Go的例子。注意用反射包實現的依賴注入

 

func TestNegroniServeHTTP(t *testing.T) {    result := ""    response := httptest.NewRecorder()     n := New()    n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {        result += "foo"        next(rw, r)        result += "ban"    }))    n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {        result += "bar"        next(rw, r)        result += "baz"    }))    n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {        result += "bat"        rw.WriteHeader(http.StatusBadRequest)    }))     n.ServeHTTP(response, (*http.Request)(nil))     expect(t, result, "foobarbatbazban")    expect(t, response.Code, http.StatusBadRequest)}

來自Negroni庫的交互式代碼片段,它是地道的Go的例子

其他語言在提供一些核心功能,比如HTTP處理的時候,往往需要依賴第三方庫。但是Go語言在這一點上很不同,它的標準庫非常強大。如果你認為Go標準庫沒有強大到可以做你想做的事情,那么我說你錯了。Go語言標準庫難以置信的強大,值得你花時間閱讀它的代碼,學習它實現的模式。

func (srv *Server) ListenAndServe() error {    addr := srv.Addr    if addr == "" {        addr = ":http"    }    ln, err := net.Listen("tcp", addr)    if err != nil {        return err    }    return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})}

Go標準庫中的ListenAndServe函數片段。如果你寫過Go程序,你可能已經調用過這個函數很多次了,但是你曾經花時間看過它的實現么?去點擊上面的代碼片段吧。

幻滅的低谷中的幻滅感來自于這樣的事實:你還在用其他語言的模式來想問題,而且你還沒有完全探索過Go能提供給你什么。下面是一些好玩的事情,你可以做一下來打破困境,進一步探索這門語言中好玩的事。

go generate

現在來看看go generate。go generate是一個你可以用來自動自成Go代碼的命令。你可以結合例如jsonenums(一個用于為枚舉類型自動生成JSON編組樣板代碼的類庫)這樣的元編程來使用go generate快速自動實現重復乏味代碼的編寫。在Go標準類庫里面已經有大量可以用于解析AST的接口,而AST使得編寫元編程工具更簡單,更容易。在會議上,有另外兩次討論(Go語言中的元編程實踐和擁抱標準類庫)談及到了這一點。

func main() {    flag.Parse()    if len(*typeNames) == 0 {        log.Fatalf("the flag -type must be set")    }    types := strings.Split(*typeNames, ",")     // Only one directory at a time can be processed, and the default is ".".    dir := "."    if args := flag.Args(); len(args) == 1 {        dir = args[0]    } else if len(args) > 1 {        log.Fatalf("only one directory at a time")    }     pkg, err := parser.ParsePackage(dir, *outputSuffix+".go")    if err != nil {        log.Fatalf("parsing package: %v", err)    }     var analysis = struct {        Command        string        PackageName    string        TypesAndValues map[string][]string    }{        Command:        strings.Join(os.Args[1:], " "),        PackageName:    pkg.Name,        TypesAndValues: make(map[string][]string),    }     // Run generate for each type.    for _, typeName := range types {        values, err := pkg.ValuesOfType(typeName)        if err != nil {            log.Fatalf("finding values for type %v: %v", typeName, err)        }        analysis.TypesAndValues[typeName] = values         var buf bytes.Buffer        if err := generatedTmpl.Execute(&buf, analysis); err != nil {            log.Fatalf("generating code: %v", err)        }         src, err := format.Source(buf.Bytes())        if err != nil {            // Should never happen, but can arise when developing this code.            // The user can compile the output to see the error.            log.Printf("warning: internal error: invalid Go generated: %s", err)            log.Printf("warning: compile the package to analyze the error")            src = buf.Bytes()        }         output := strings.ToLower(typeName + *outputSuffix + ".go")        outputPath := filepath.Join(dir, output)        if err := ioutil.WriteFile(outputPath, src, 0644); err != nil {            log.Fatalf("writing output: %s", err)        }    }}

一段互動的片段演示了如何編寫jsonenums命令。

OpenGL

許多人使用Go作web服務,但是你知道你也可以用Go寫出很cool的圖形應用嗎?查看Go在OpenGL中的捆綁。

func main() {    glfw.SetErrorCallback(errorCallback)     if !glfw.Init() {        panic("Can't init glfw!")    }    defer glfw.Terminate()     window, err := glfw.CreateWindow(Width, Height, Title, nil, nil)    if err != nil {        panic(err)    }     window.MakeContextCurrent()     glfw.SwapInterval(1)     gl.Init()     if err := initScene(); err != nil {        fmt.Fprintf(os.Stderr, "init: %s/n", err)        return    }    defer destroyScene()     for !window.ShouldClose() {        drawScene()        window.SwapBuffers()        glfw.PollEvents()    }}

交互式的片段正說明Go的OpenGL捆綁能制作Gopher cube。點擊函數或方法名去探索。

黑客馬拉松和挑戰

你也可以觀看挑戰和黑客馬拉松,類似Gopher Gala和Go Challenge。在過去,來自世界各地的程序員一起挑戰一些真實的酷項目,你可以從中獲取靈感。

第三階段: 老手

作為一個老手,這意味著你可以解決很多Go語言中你關心的問題。新的需要解決的問題會帶來新的疑問,經過試錯,你學會了在這門語言中什么是可以做的,什么是不能做的。此時,你已經對這門語言的習慣和模式有了一個堅實的理解。你可以非常高效地工作,寫出可讀,文檔完善,可維護的代碼。

成為老手的一個很好的方法就是在大項目上工作。如果你自己有一個項目的想法,開始動手去做吧(當然你要確定它并不是已經存在了)。大多數人也許并沒有一個很大的項目的想法,所以他們可以對已經存在的項目做出貢獻。Go語言已經有很多大型項目,而且它們正在被廣泛使用,比如Docker, Kubernetes和Go本身??梢钥纯催@個項目列表

func (cli *DockerCli) CmdRestart(args ...string) error {    cmd := cli.Subcmd("restart", "CONTAINER [CONTAINER...]", "Restart a running container", true)    nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing the container.")    cmd.Require(flag.Min, 1)     utils.ParseFlags(cmd, args, true)     v := url.Values{}    v.Set("t", strconv.Itoa(*nSeconds))     var encounteredError error    for _, name := range cmd.Args() {        _, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, false))        if err != nil {            fmt.Fprintf(cli.err, "%s/n", err)            encounteredError = fmt.Errorf("Error: failed to restart one or more containers")        } else {            fmt.Fprintf(cli.out, "%s/n", name)        }    }    return encounteredError}

Docker項目的交互式代碼片段。點擊函數名,開始探索之旅吧。

老手應該對Go生態系統的工具有一個很強的掌握,因為這些工具真的提高生產效率。你應該了解go generate,go vet,go test-race, 和gofmt/goimports/goreturns。你應該使用go fmt,因為它會自動把你的代碼按照Go社區的風格標準來格式化。goimports可以做同樣的事情,而且還會添加丟失的imports。goretures不光做了前面所說的事情,還可以在返回表達式添加丟失的錯誤,這是大家都討厭的地方。

在老手階段,你一定要開始做code review。code review的意義并不是要修改或者找到錯誤(那是測試人員做的事情)。code review可以幫助維持統一的編程風格,提高軟件的總體質量,還可以在別人的反饋中提高你自己的編程技術。幾乎所有的大型開源項目都對每一個提交做code review。

下面是一個從人類的反饋當中學習的例子:Google的Go團隊以前都在main函數的外面聲明命令行標記。在去年的GopherCon會議上,Francesc遇到了SoundCloud公司的Peter Bourgon(@peterbourgon)。Peter Bourgon說在SoundCloud,他們都在main函數內部聲明標記,這樣他們不會錯誤地在外部使用標記。Francesc現在認為這是最佳實踐。

第四階段:專家

作為一個專家,你很好地了解了語言的哲學思想。對于Go語言的特性,你知道何時應該使用,何時不應該使用。例如,Jeremy Saenz在dotGo風暴討論中談論到了何時不該使用接口。

func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call {    call := new(Call)    call.ServiceMethod = serviceMethod    call.Args = args    call.Reply = reply    if done == nil {        done = make(chan *Call, 10) // buffered.    } else {        // If caller passes done != nil, it must arrange that        // done has enough buffer for the number of simultaneous        // RPCs that will be using that channel.  If the channel        // is totally unbuffered, it's best not to run at all.        if cap(done) == 0 {            log.Panic("rpc: done channel is unbuffered")        }    }    call.Done = done    client.send(call)    return call}

來自標準類庫的一小塊交互代碼片段使用了頻道。理解標準類庫里面的模式背后的決策原因是成為一個專家必經之路。

但是不要成為只局限于單一語言的專家。跟其他任何語言一樣,Go僅僅只是一個工具。你還應該去探索其他語言,并且學習他們的模式和風格。Francesc從他使用Go的經驗中找到了編寫JavaScript的啟發。他還喜歡重點關注于不可變性和致力于避免易變性的Haskell語言,并從中獲得了如何編寫Go代碼的靈感。

布道者

作為一個布道者,你分享自己的知識,傳授你學會的和你提出的最佳實踐。你可以分享自己對Go喜歡或者不喜歡的地方。全世界各地都有Go會議,找到離你最近的。

你可以在任何一個階段成為布道者,不要等到你成為這個領域的專家的時候才發出自己的聲音。在你學習Go的任何一個階段,提出問題,結合你的經驗給出反饋,不要羞于提出自己不喜歡的地方。你提出的反饋可以幫助社區改善做事情的方法,也可能改變你自己對編程的看法。

func main() {    httpAddr := flag.String("http", "127.0.0.1:3999", "HTTP service address (e.g., '127.0.0.1:3999')")    originHost := flag.String("orighost", "", "host component of web origin URL (e.g., 'localhost')")    flag.StringVar(&basePath, "base", "", "base path for slide template and static resources")    flag.BoolVar(&present.PlayEnabled, "play", true, "enable playground (permit execution of arbitrary user code)")    nativeClient := flag.Bool("nacl", false, "use Native Client environment playground (prevents non-Go code execution)")    flag.Parse()     if basePath == "" {        p, err := build.Default.Import(basePkg, "", build.FindOnly)        if err != nil {            fmt.Fprintf(os.Stderr, "Couldn't find gopresent files: %v/n", err)            fmt.Fprintf(os.Stderr, basePathMessage, basePkg)            os.Exit(1)        }        basePath = p.Dir    }    err := initTemplates(basePath)    if err != nil {        log.Fatalf("Failed to parse templates: %v", err)    }     ln, err := net.Listen("tcp", *httpAddr)    if err != nil {        log.Fatal(err)    }    defer ln.Close()     _, port, err := net.SplitHostPort(ln.Addr().String())    if err != nil {        log.Fatal(err)    }    origin := &url.URL{Scheme: "http"}    if *originHost != "" {        origin.Host = net.JoinHostPort(*originHost, port)    } else if ln.Addr().(*net.TCPAddr).IP.IsUnspecified() {        name, _ := os.Hostname()        origin.Host = net.JoinHostPort(name, port)    } else {        reqHost, reqPort, err := net.SplitHostPort(*httpAddr)        if err != nil {            log.Fatal(err)        }        if reqPort == "0" {            origin.Host = net.JoinHostPort(reqHost, port)        } else {            origin.Host = *httpAddr        }    }     if present.PlayEnabled {        if *nativeClient {            socket.RunScripts = false            socket.Environ = func() []string {                if runtime.GOARCH == "amd64" {                    return environ("GOOS=nacl", "GOARCH=amd64p32")                }                return environ("GOOS=nacl")            }        }        playScript(basePath, "SocketTransport")        http.Handle("/socket", socket.NewHandler(origin))    }    http.Handle("/static/", http.FileServer(http.Dir(basePath)))     if !ln.Addr().(*net.TCPAddr).IP.IsLoopback() &&        present.PlayEnabled && !*nativeClient {        log.Print(localhostWarning)    }     log.Printf("Open your web browser and visit %s", origin.String())    log.Fatal(http.Serve(ln, nil))

流行的present命令的main函數,很多Go的用戶使用它來制作幻燈片。許多演講者修改了這個模塊來滿足自己的需要。

Q&A

問:在GO語言中,我所懷念的一項功能是一個好的調試器。

答:我們正在做了,不只是調試器,我們還會提供一個更好的總體監視工具可以讓你在程序運行時更好地洞察程序在干什么(顯示出所有正在運行的goroutine的狀態)。在GO 1.5中探索它吧。

以上所述就是本文的全部內容了,希望大家能夠喜歡。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
搡老女人一区二区三区视频tv| 国产日产欧美a一级在线| 尤物yw午夜国产精品视频明星| 国产精品成人一区二区三区吃奶| 国产热re99久久6国产精品| 亚洲一级黄色av| 亚洲欧美日韩一区二区三区在线| 久久精品电影网站| 欧美性猛交xxxx免费看久久久| 欧美黑人一区二区三区| 精品国产一区二区三区久久狼5月| 日韩激情av在线免费观看| 国产日韩欧美一二三区| 国产精品偷伦一区二区| 欧美激情videos| 性夜试看影院91社区| 日韩精品中文在线观看| 国产精品欧美久久久| 精品无码久久久久久国产| 91亚洲精品在线| 久久久亚洲福利精品午夜| 欧美视频在线视频| 欧美在线视频a| 成人黄色影片在线| 亚洲自拍偷拍色片视频| 亚洲一品av免费观看| 亚洲理论在线a中文字幕| 国产日韩精品电影| 国产精品极品美女粉嫩高清在线| 2024亚洲男人天堂| 最新亚洲国产精品| 一区二区在线视频| 亚洲欧洲在线播放| 国产成人在线一区二区| 国产精品香蕉国产| 国产精品视频大全| 亚洲国内精品视频| 久久久精品在线| 亚洲一区二区在线播放| 日韩色av导航| 久久在线免费视频| 一区二区欧美亚洲| 久久伊人精品天天| 日韩av一区在线观看| 亚洲精品v欧美精品v日韩精品| 久久免费视频在线观看| 亚洲黄色在线看| 久久激情视频久久| 亚洲欧美日韩久久久久久| 日韩av中文字幕在线| 国产精品盗摄久久久| 日本精品久久中文字幕佐佐木| 国产精品丝袜久久久久久高清| 精品久久久久久久久久ntr影视| 日韩精品小视频| 色综合老司机第九色激情| 精品国产自在精品国产浪潮| 欧美性xxxxxx| 午夜精品福利在线观看| 欧美大片欧美激情性色a∨久久| 韩国美女主播一区| 亚洲va欧美va在线观看| 亚洲精品www| 日韩中文字幕在线播放| xvideos亚洲人网站| 午夜免费在线观看精品视频| 日韩av电影手机在线观看| 欧美一区二区三区图| 亚洲精品福利视频| 欧美视频精品一区| 欧美国产精品va在线观看| 欧美裸体男粗大视频在线观看| 九九热这里只有精品免费看| 美女999久久久精品视频| 伊人成人开心激情综合网| 国产精品高潮呻吟久久av无限| 2019中文字幕在线观看| 日韩欧美国产视频| 欧美专区在线观看| 欧美一级bbbbb性bbbb喷潮片| 国产激情久久久| 精品中文字幕在线2019| 欧美大片大片在线播放| 欧美成人黑人xx视频免费观看| 欧美一级高清免费| 国产精品久久av| 国产视频精品久久久| 日韩中文字幕久久| 久久免费国产视频| 91精品久久久久久久久久久久久久| 大桥未久av一区二区三区| 欧美亚洲国产日韩2020| 亚洲日韩中文字幕| y97精品国产97久久久久久| 米奇精品一区二区三区在线观看| 狠狠躁夜夜躁人人爽天天天天97| 日韩国产精品一区| 91精品国产91久久久久| 成人免费网站在线看| 日本精品视频在线播放| 欧美日韩美女在线观看| 国产精品一香蕉国产线看观看| 欧美视频一二三| 亚洲成人激情在线| 亚洲精品美女久久| 精品久久久久久中文字幕大豆网| 亚洲欧美国产精品专区久久| 全色精品综合影院| 亚洲免费成人av电影| 日韩精品一二三四区| 国产婷婷色综合av蜜臀av| 成人亚洲激情网| 国产精品久久久久久久美男| 国产精品v片在线观看不卡| 欧美电影《睫毛膏》| 2019亚洲日韩新视频| 久久久久国产精品免费| 韩日精品中文字幕| 久久久亚洲福利精品午夜| 欧美精品成人91久久久久久久| 日韩资源在线观看| 久久久亚洲成人| 久久91亚洲人成电影网站| 久久久久免费视频| 中文字幕一区日韩电影| 成人黄色av网| 亚洲高清一二三区| 6080yy精品一区二区三区| 国产极品精品在线观看| 国产亚洲欧美日韩精品| 日韩成人av一区| 成人亚洲欧美一区二区三区| 亚洲精品99久久久久中文字幕| 国产一级揄自揄精品视频| 国产精品午夜一区二区欲梦| xxxxxxxxx欧美| 中文字幕国内精品| 午夜精品美女自拍福到在线| 欧美成人在线网站| 91禁国产网站| 欧美一二三视频| 亚洲成人黄色在线观看| 欧美日韩国产在线看| 亚洲国产精品国自产拍av秋霞| 久久电影一区二区| 欧美在线激情视频| 亚洲天堂av在线播放| 伊人久久免费视频| 国产精品女人久久久久久| 亚洲精品日韩欧美| 亚洲春色另类小说| 精品自在线视频| 久久在线免费观看视频| 色系列之999| 国产精品1区2区在线观看| 亚洲第一色在线| 国产精品扒开腿做| 亚洲午夜激情免费视频| …久久精品99久久香蕉国产| 日韩精品在线观| 亚洲人成网7777777国产| 亚洲精品日韩欧美| 国内精品久久久久久久久| 成人黄色av播放免费|