Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Go实战--golang中使用RethinkDB(gorethink/gorethink.v3)

Go实战--golang中使用RethinkDB(gorethink/gorethink.v3)

作者头像
程序员的酒和故事
发布于 2018-03-12 09:49:38
发布于 2018-03-12 09:49:38
1.9K00
代码可运行
举报
运行总次数:0
代码可运行

生命不止,继续go go go !!!

关于golang中操作数据库,曾经介绍了不少:

Go实战–go语言操作sqlite数据库(The way to go) Go实战–go语言操作MySQL数据库(go-sql-driver/mysql)

Go实战–golang中使用redis(redigo和go-redis/redis) Go实战–golang中使用MongoDB(mgo)

今天继续跟大家一起学习分享另一种数据库叫 RethinkDB。

RethinkDB

RethinkDB 是一个主要用来存储 JSON 文档的数据库引擎(MongoDB 存储的是 BSON),可以轻松和多个节点连成分布式数据库,非常好用的查询语言以及支持表的 joins 和 group by 操作等,其实跟mongodb类似。

RethinkDB pushes JSON to your apps in realtime. When your app polls for data, it becomes slow, unscalable, and cumbersome to maintain. RethinkDB is the open-source, scalable database that makes building realtime apps dramatically easier.

What is RethinkDB? RethinkDB is the first open-source, scalable JSON database built from the ground up for the realtime web. It inverts the traditional database architecture by exposing an exciting new access model – instead of polling for changes, the developer can tell RethinkDB to continuously push updated query results to applications in realtime. RethinkDB’s realtime push architecture dramatically reduces the time and effort necessary to build scalable realtime apps.

In addition to being designed from the ground up for realtime apps, RethinkDB offers a flexible query language, intuitive operations and monitoring APIs, and is easy to setup and learn.

官网 https://www.rethinkdb.com/

Windows下安装 下载地址: https://www.rethinkdb.com/docs/install/windows/

解压

创建数据目录: d:\RethinkDB\data\

运行命令:

代码语言:text
AI代码解释
复制
rethinkdb.exe -d d:\RethinkDB\data\

成功

代码语言:text
AI代码解释
复制
In recursion: removing file 'd:\RethinkDB\data\\tmp'
warn: Trying to delete non-existent file 'd:\RethinkDB\data\\tmp'
Initializing directory d:\RethinkDB\data\
Running rethinkdb 2.3.6-windows (MSC 190024215)...
Running on 6.2.9200 (Windows 8, Server 2012)
Loading data from directory d:\RethinkDB\data\
Listening for intracluster connections on port 29015
Listening for client driver connections on port 28015
Listening for administrative HTTP connections on port 8080
Listening on cluster address: 127.0.0.1
Listening on driver address: 127.0.0.1
Listening on http address: 127.0.0.1
To fully expose RethinkDB on the network, bind to all addresses by running rethinkdb with the `--bind all` command line option.
Server ready, "LAPTOP_MNU6522J_xsq" b8612d2e-7c2b-4511-b85a-17468d91bf6d

可视化 http://localhost:8080

GoRethink - RethinkDB Driver for Go

github地址: https://github.com/GoRethink/gorethink

Star: 1253

获取:

代码语言:c#
AI代码解释
复制
go get gopkg.in/gorethink/gorethink.v3

文档地址: https://godoc.org/github.com/GoRethink/gorethink

ConnectOpts

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type ConnectOpts struct {

    Address string `gorethink:"address,omitempty"`

    Addresses []string `gorethink:"addresses,omitempty"`

    Database string `gorethink:"database,omitempty"`

    Username string `gorethink:"username,omitempty"`

    Password string `gorethink:"password,omitempty"`

    AuthKey string `gorethink:"authkey,omitempty"`

    Timeout time.Duration `gorethink:"timeout,omitempty"`

    WriteTimeout time.Duration `gorethink:"write_timeout,omitempty"`

    ReadTimeout time.Duration `gorethink:"read_timeout,omitempty"`

    KeepAlivePeriod time.Duration `gorethink:"keep_alive_timeout,omitempty"`

    TLSConfig *tls.Config `gorethink:"tlsconfig,omitempty"`

    HandshakeVersion HandshakeVersion `gorethink:"handshake_version,omitempty"`

    UseJSONNumber bool

    NumRetries int

    InitialCap int `gorethink:"initial_cap,omitempty"`

    MaxOpen int `gorethink:"max_open,omitempty"`

    DiscoverHosts bool `gorethink:"discover_hosts,omitempty"`

    HostDecayDuration time.Duration

    NodeRefreshInterval time.Duration `gorethink:"node_refresh_interval,omitempty"`

    MaxIdle int `gorethink:"max_idle,omitempty"`
}

func Connect Connect creates a new database session.

代码语言:lua
AI代码解释
复制
func Connect(opts ConnectOpts) (*Session, error)

func Expr Expr converts any value to an expression and is also used by many other terms such as Insert and Update. This function can convert the following basic Go types (bool, int, uint, string, float) and even pointers, maps and structs.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func Expr(val interface{}) Term

func (Term) Run

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (t Term) Run(s QueryExecutor, optArgs ...RunOpts) (*Cursor, error)

Run runs a query using the given connection.

func (*Cursor) One

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (c *Cursor) One(result interface{}) error

One retrieves a single document from the result set into the provided slice and closes the cursor.

func DB

代码语言:go
AI代码解释
复制
func DB(args ...interface{}) Term

DB references a database.

func TableDrop

代码语言:go
AI代码解释
复制
func TableDrop(args ...interface{}) Term

TableDrop deletes a table. The table and all its data will be deleted.

func TableCreate

代码语言:php
AI代码解释
复制
func TableCreate(name interface{}, optArgs ...TableCreateOpts) Term

TableCreate creates a table. A RethinkDB table is a collection of JSON documents.

官方例子 main.go

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import (
    "fmt"
    "log"

    r "gopkg.in/gorethink/gorethink.v3"
)

func main() {
    session, err := r.Connect(r.ConnectOpts{
        Address: "localhost:28015",
    })
    if err != nil {
        log.Fatalln(err)
    }

    res, err := r.Expr("Hello World").Run(session)
    if err != nil {
        log.Fatalln(err)
    }

    var response string
    err = res.One(&response)
    if err != nil {
        log.Fatalln(err)
    }

    fmt.Println(response)
}

如果rethinkdb服务没有开启则: 2017/12/12 14:37:34 gorethink: dial tcp [::1]:28015: connectex: No connection could be made because the target machine actively refused it.

开启后运行: Hello World

rethinkdb应用

访问: http://localhost:8080/#tables 创建一个database,命名为players

读写数据库

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import (
    "fmt"
    "log"
    "math/rand"
    "strconv"
    "time"

    r "gopkg.in/gorethink/gorethink.v3"
)

//ScoreEntry for scores
type ScoreEntry struct {
    ID         string `gorethink:"id,omitempty"`
    PlayerName string
    Score      int
}

func main() {
    fmt.Println("Connecting to RethinkDB: localhost:28015")

    session, err := r.Connect(r.ConnectOpts{
        Address:  "localhost:28015",
        Database: "players",
    })

    if err != nil {
        log.Fatal("Could not connect")
    }

    err = r.DB("players").TableDrop("scores").Exec(session)
    err = r.DB("players").TableCreate("scores").Exec(session)
    if err != nil {
        log.Fatal("Could not create table")
    }

    err = r.DB("players").Table("scores").IndexCreate("Score").Exec(session)
    if err != nil {
        log.Fatal("Could not create index")
    }

    for i := 0; i < 1000; i++ {
        player := new(ScoreEntry)
        player.ID = strconv.Itoa(i)
        player.PlayerName = fmt.Sprintf("Player %d", i)
        player.Score = rand.Intn(100)
        _, err := r.Table("scores").Insert(player).RunWrite(session)
        if err != nil {
            log.Fatal(err)
        }
    }

    for {
        var scoreentry ScoreEntry
        pl := rand.Intn(1000)
        sc := rand.Intn(6) - 2
        res, err := r.Table("scores").Get(strconv.Itoa(pl)).Run(session)
        if err != nil {
            log.Fatal(err)
        }

        err = res.One(&scoreentry)
        scoreentry.Score = scoreentry.Score + sc
        _, err = r.Table("scores").Update(scoreentry).RunWrite(session)
        time.Sleep(100 * time.Millisecond)
    }
}

可以通过localhost:8080可视化查看:

RethinkDB的CRUD

再来一个比较复杂的例子,代码结构会更好一点: bookmarket_store.go 其中包括了: create update Delete getAll GetByID

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import (
    "time"

    r "gopkg.in/gorethink/gorethink.v3"
)

// Bookmark type reperesents the metadata of a bookmark.
type Bookmark struct {
    ID                          string `gorethink:"id,omitempty" json:"id"`
    Name, Description, Location string
    Priority                    int // Priority (1 -5)
    CreatedOn                   time.Time
    Tags                        []string
}

// BookmarkStore provides CRUD operations against the Table "bookmarks".
type BookmarkStore struct {
    Session *r.Session
}

// Create inserts the value of struct Bookmark into Table.
func (store BookmarkStore) Create(b *Bookmark) error {

    resp, err := r.Table("bookmarks").Insert(b).RunWrite(store.Session)
    if err == nil {
        b.ID = resp.GeneratedKeys[0]
    }

    return err
}

// Update modifies an existing value of a Table.
func (store BookmarkStore) Update(b Bookmark) error {

    var data = map[string]interface{}{
        "description": b.Description,
        "location":    b.Location,
        "priority":    b.Priority,
        "tags":        b.Tags,
    }
    // partial update on RethinkDB
    _, err := r.Table("bookmarks").Get(b.ID).Update(data).RunWrite(store.Session)
    return err
}

// Delete removes an existing value from the Table.
func (store BookmarkStore) Delete(id string) error {
    _, err := r.Table("bookmarks").Get(id).Delete().RunWrite(store.Session)
    return err
}

// GetAll returns all documents from the Table.
func (store BookmarkStore) GetAll() ([]Bookmark, error) {
    bookmarks := []Bookmark{}

    res, err := r.Table("bookmarks").OrderBy("priority", r.Desc("createdon")).Run(store.Session)
    err = res.All(&bookmarks)
    return bookmarks, err
}

// GetByID returns single document from the Table.
func (store BookmarkStore) GetByID(id string) (Bookmark, error) {
    var b Bookmark
    res, err := r.Table("bookmarks").Get(id).Run(store.Session)
    res.One(&b)
    return b, err
}

main.go

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import (
    "fmt"
    "log"
    "time"

    r "gopkg.in/gorethink/gorethink.v3"
)

var store BookmarkStore
var id string

func initDB(session *r.Session) {
    var err error
    // Create Database
    _, err = r.DBCreate("bookmarkdb").RunWrite(session)
    if err != nil {
        log.Fatalf("[initDB]: %s\n", err)
    }
    // Create Table
    _, err = r.DB("bookmarkdb").TableCreate("bookmarks").RunWrite(session)
    if err != nil {
        log.Fatalf("[initDB]: %s\n", err)
    }
}

func changeFeeds(session *r.Session) {
    bookmarks, err := r.Table("bookmarks").Changes().Field("new_val").Run(session)
    if err != nil {
        log.Fatalf("[changeFeeds]: %s\n", err)
    }
    // Luanch a goroutine to print real-time updates.
    go func() {
        var bookmark Bookmark
        for bookmarks.Next(&bookmark) {
            if bookmark.ID == "" { // for delete, new_val will be null.
                fmt.Println("Real-time update: Document has been deleted")
            } else {
                fmt.Printf("Real-time update: Name:%s, Description:%s, Priority:%d\n",
                    bookmark.Name, bookmark.Description, bookmark.Priority)
            }
        }
    }()
}

func init() {
    session, err := r.Connect(r.ConnectOpts{
        Address:  "localhost:28015",
        Database: "bookmarkdb",
        MaxIdle:  10,
        MaxOpen:  10,
    })

    if err != nil {
        log.Fatalf("[RethinkDB Session]: %s\n", err)
    }
    r.Table("bookmarks").Delete().Run(session)
    // Create Database and Table.
    initDB(session)
    store = BookmarkStore{
        Session: session,
    }
    // Subscribe real-time changes
    changeFeeds(session)
}

func createUpdate() {
    bookmark := Bookmark{
        Name:        "mgo",
        Description: "Go driver for MongoDB",
        Location:    "https://github.com/go-mgo/mgo",
        Priority:    1,
        CreatedOn:   time.Now(),
        Tags:        []string{"go", "nosql", "mongodb"},
    }
    // Insert a new document.
    if err := store.Create(&bookmark); err != nil {
        log.Fatalf("[Create]: %s\n", err)
    }
    id = bookmark.ID
    fmt.Printf("New bookmark has been inserted with ID: %s\n", id)
    // Retrieve the updated document.
    bookmark.Priority = 2
    if err := store.Update(bookmark); err != nil {
        log.Fatalf("[Update]: %s\n", err)
    }
    fmt.Println("The value after update:")
    // Retrieve an existing document by id.
    getByID(id)
    bookmark = Bookmark{
        Name:        "gorethink",
        Description: "Go driver for RethinkDB",
        Location:    "https://github.com/dancannon/gorethink",
        Priority:    1,
        CreatedOn:   time.Now(),
        Tags:        []string{"go", "nosql", "rethinkdb"},
    }
    // Insert a new document.
    if err := store.Create(&bookmark); err != nil {
        log.Fatalf("[Create]: %s\n", err)
    }
    id = bookmark.ID
    fmt.Printf("New bookmark has been inserted with ID: %s\n", id)

}

func getByID(id string) {
    bookmark, err := store.GetByID(id)
    if err != nil {
        log.Fatalf("[GetByID]: %s\n", err)
    }
    fmt.Printf("Name:%s, Description:%s, Priority:%d\n", bookmark.Name, bookmark.Description, bookmark.Priority)
}

func getAll() {
    // Layout for formatting dates.
    layout := "2006-01-02 15:04:05"
    // Retrieve all documents.
    bookmarks, err := store.GetAll()
    if err != nil {
        log.Fatalf("[GetAll]: %s\n", err)
    }
    fmt.Println("Read all documents")
    for _, v := range bookmarks {
        fmt.Printf("Name:%s, Description:%s, Priority:%d, CreatedOn:%s\n", v.Name, v.Description, v.Priority, v.CreatedOn.Format(layout))
    }

}

func delete() {
    if err := store.Delete(id); err != nil {
        log.Fatalf("[Delete]: %s\n", err)
    }
    bookmarks, err := store.GetAll()
    if err != nil {
        log.Fatalf("[GetAll]: %s\n", err)
    }
    fmt.Printf("Number of documents in the table after delete:%d\n", len(bookmarks))
}

func main() {
    createUpdate()
    getAll()
    delete()
}

输出:

代码语言:css
AI代码解释
复制
Real-time update: Name:mgo, Description:Go driver for MongoDB, Priority:1
New bookmark has been inserted with ID: 1f98916d-a5d5-400b-828e-8a53d4193521
Real-time update: Name:mgo, Description:Go driver for MongoDB, Priority:2
The value after update:
Name:mgo, Description:Go driver for MongoDB, Priority:1
Real-time update: Name:gorethink, Description:Go driver for RethinkDB, Priority:1
New bookmark has been inserted with ID: 0da9d082-265c-40e8-af54-7210a78cdb19
Read all documents
Name:gorethink, Description:Go driver for RethinkDB, Priority:1, CreatedOn:2017-12-12 15:10:13
Name:mgo, Description:Go driver for MongoDB, Priority:2, CreatedOn:2017-12-12 15:10:13
Real-time update: Document has been deleted
Number of documents in the table after delete:1

悲伤的消息

Today(OCTOBER 05, 2016) I have sad news to share. After more than seven years of development, the company behind RethinkDB is shutting down. We worked very hard to make RethinkDB successful, but in spite of all our efforts we were ultimately unable to build a sustainable business. There is a lot of information to unpack – over the next few months, I’ll write about lessons learned so the startup community can benefit from our mistakes.

如何评价RethinkDB公司倒闭? https://www.zhihu.com/question/51345388?sort=created

尾声: RethinkDB: why we failed 结论: Pick a large market but build for specific users. Learn to recognize the talents you’re missing, then work like hell to get them on your team. Read The Economist religiously. It will make you better faster.

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-12-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 人生有味是多巴胺 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
go中使用ssh
在Go语言中,golang.org/x/crypto/ssh 是一个官方维护的第三方库,用于实现 SSH 客户端和服务器功能。本文我们将学习如何使用该库建立 SSH 连接、执行远程命令、模拟终端交互等常见操作。
孟斯特
2025/04/21
980
go中使用ssh
007-golang-GO 编程高手的五个阶段
第一个阶段(菜鸟): 刚刚学习了这门语言。 已经通过一些教程或者培训班了解基本的语法,可以写短的代码片段。
上善若水.夏
2018/09/28
1.2K0
Asynq: 基于Redis实现的Go生态分布式任务队列和异步处理库
Asynq[1]是一个Go实现的分布式任务队列和异步处理库,基于redis,类似Ruby的sidekiq[2]和Python的celery[3]。Go生态类似的还有machinery[4]和goworker
fliter
2023/09/05
1.1K0
Asynq: 基于Redis实现的Go生态分布式任务队列和异步处理库
Golang深入浅出之-Go语言中的服务注册与发现机制
服务注册与发现是微服务架构中的重要组件,它允许服务实例在启动时向注册中心注册自己,同时其他服务能够动态地发现并调用这些已注册的服务。在Go语言生态中,这一机制的实现尤为关键,本文将深入浅出地探讨其原理、常见问题、易错点及解决方案,并提供代码示例。
Jimaks
2024/05/04
2930
Golang 对MongoDB的操作简单封装
每一次操作都copy一份 Session,避免每次创建Session,导致连接数量超过设置的最大值 获取文档对象c := Session.DB(db).C(collection)
程序员同行者
2019/02/22
2.4K0
golang办公流程引擎初体验js-ojus/flow——系列三
1.管理员定义好流程类型doctype,这个下面再分流程类型workflow1,workflow2,workflow下再具体分为节点node1,node2,
hotqin888
2019/02/25
1.9K0
golang办公流程引擎初体验js-ojus/flow——系列三
虚拟机自动化注意了
使用开源项目:github.com/vmware/govmomi 来实现虚拟机自动化部署流程
heidsoft
2022/01/11
4430
虚拟机自动化注意了
golang实现http2.0服务端,客户端完整案例
为了学习golang的client源码执行流程,所以笔者通过golang实现http2的服务端和客户端,然后通过单步调试学习源码。下面我们看下整个golang实现http2的服务步骤。
公众号-利志分享
2022/04/25
5.2K0
k8s first commit 源码分析之 Cloudcfg
cloudcfg 可以看做是 kubectl 的前身,负责与 API server 的交互,只存在于上古时代的 k8s 中,我们现在接触到的都是叫做 kubectl 的命令行工具了。该组件做的事情非常简单,就是将用户的命令行操作转化为对 API server 的 HTTP 请求。
菜菜cc
2022/11/15
2740
golang源码分析:grpc 链接池(1)
上面几个问题,看着都觉得这么简单还用问?但是仔细一想,内心还是有些不太确定。因为没有分析过源码!下面我们带着问题来进行研究。我们生成一段代码,启动一个server
golangLeetcode
2023/03/01
1K0
golang源码分析:grpc 链接池(1)
原 Go 的 MogonDB 包 mgo
gopkg.in/mgo.v2 库是go语言里对应着MogonDB的库,好似这个推荐的人很多,比go对mysql的库统一多了 一、连接数据库 // 注意包的引用 const URL = "" //mongodb连接字符串 var ( mgoSession *mgo.Session dataBase = "mydb" ) /** * 公共方法,获取session,如果存在则拷贝一份 */ func getSession() *mgo.Session { if mgoSession ==
霡霂
2018/06/04
1.7K0
中文全文检索技术路线(elasticsearch全文检索、中文分词ik、tika解析文档)
代码在开源仓库3xxxhttps://github.com/3xxx/engineercms
hotqin888
2021/12/06
9780
中文全文检索技术路线(elasticsearch全文检索、中文分词ik、tika解析文档)
ent orm笔记4---Code Generation
在前面几篇文章中,我们经常使用的可能就是entc这个命令了,entc这个工具给带来了很多功能,这篇文章主要整理关于ent orm 中Code Generation
coders
2020/09/03
6940
ent orm笔记4---Code Generation
gRPC如何在Golang和PHP中进行实战?7步教你上手!
导语 | gRPC也是RPC技术家族的一种,它由Google主导开发,是一个跨平台的调用框架,其中和go语言结合的是最紧密的,在go语言的开发和调用中占据主导地位。gRPC采用protobuf作为配置载体来实现通讯和调用。本文主要实战演示一下gRPC的几种调用通讯模式(普通、客户端流、服务端流、双向流)以及和PHP客户端的联通调用。 在学习gRPC之前,我们需要了解一下ptorobuf语法和protoc的命令,能帮助我们更加深入的学习和理解gRPC。 一、需求分析 我们这次只搞个很简单的需求,搞个用户
腾讯云开发者
2021/10/20
2.9K1
golang集成测试:dockertest testcontainers-go
在做集成测试的时候,每次测试前,如果通过docker重启一个干净的容器是不是免去了数据清理的苦恼。https://github.com/testcontainers/testcontainers-go和https://github.com/ory/dockertest可以解决我们的苦恼,它们很相似都是调用docker的api实现镜像的拉取和容器的启动关闭。然后我们可以基于容器做对应的集成测试。
golangLeetcode
2023/03/01
7980
golang集成测试:dockertest testcontainers-go
Golang ast 的使用
从文件中获取注释信息 package main import ( "go/ast" "go/parser" "go/token" "log" "path/filepath" ) type Visitor struct { fset *token.FileSet } func (v *Visitor) Visit(node ast.Node) ast.Visitor { switch node.(type) { //判断ast分类 case *ast.FuncDecl: de
码缘
2022/12/14
9490
Go语言中的JSON处理 【Go语言圣经笔记】
JavaScript对象简谱(JSON, Java Script Object Notation)是一种用于发送和接收结构化信息的标准协议。在类似的协议中,JSON并不是唯一的一个标准协议。 XML(§7.14)、ASN.1和Google的Protocol Buffers都是类似的协议,并且有各自的特色,但是由于简洁性、可读性和流行程度等原因,JSON是应用最广泛的一个。
Steve Wang
2021/12/06
6410
Go + gRPC-Gateway(V2) 构建微服务实战系列,小程序登录鉴权服务:第一篇(内附开发 demo)
小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识,快速建立小程序内的用户体系。
为少
2021/05/27
1.3K0
Go + gRPC-Gateway(V2) 构建微服务实战系列,小程序登录鉴权服务:第一篇(内附开发 demo)
golang学习之mgo操作mongodb
mgo是mongodb的golang驱动,测试代码: // mgotest project main.go package main import ( "fmt" "time"
用户1141560
2017/12/26
2.1K0
golang-xorm库快速学习
xorm xorm是一个Go语言ORM库. 通过它可以使数据库操作非常简便. 全部文档点我 用法入门: 前提:定义本文中用到的struct和基本代码如下 // 银行账户 type Account struct { Id int64 Name string `xorm:"unique"` Balance float64 Version int `xorm:"version"` // 乐观锁 } var x *xorm.Engine 创建orm引擎 注意:若想
李海彬
2018/03/28
2.7K0
golang-xorm库快速学习
推荐阅读
相关推荐
go中使用ssh
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验