[golang 微服务] 5. 微服务服务发现介绍,安装以及consul的使用,Consul集群

news2024/11/26 23:50:09

一.服务发现介绍

  1. 引入

上一节讲解了使用 gRPC创建微服务,客户端的一个接口可能需要调用 N个服务,而不同服务可能存在 不同的服务器,这时,客户端就必须知道所有服务的 网络位置(ip+port),来进行连接服务器操作,如下图所示:

以往的做法是把服务的地址放在配置文件或者数据库中,这样就有以下几个问题:

  • 需要配置N个服务的网络位置,加大配置的复杂性

  • 服务的网络位置变化,需要改变每个调用者的配置

  • 集群的情况下,难以做负载(反向代理的方式除外)

总结起来一句话: 服务多了,配置很麻烦,问题一大堆

所以现在就选择服务发现来解决这些问题,具体设计如下:

与之前解决方法不同的是,加了个 服务发现模块,服务端把当前自己的网络位置 注册到服务发现模块(这里注册的意思就是告诉),服务发现就以 K-V的方式记录下,K一般是 服务名,V就是 IP:PORT,服务发现模块 定时的轮询查看这些服务能不能访问的了(这就是 健康检查),客户端在调用服务A-N的时候,就跑去服务发现模块问下它们的网络位置,然后再调用它们的服务,这样的方式就可以解决上面的问题了, 客户端完全不需要记录这些服务的网络位置, 客户端和服务端完全解耦

常见的服务发现框架有:Etcd、mdns、Consul、Zookeeper

  • consul 常应用于grpc 、 go-micro

  • mdns:以前mdns是go-micro中默认自带的服务发现go-micro的默认服务发现也是consul

  • etcd:k8s 内嵌的服务发现

  • zookeeper:java中较常用

这里选择服务发现框架consul来做一个详细介绍

  1. consul介绍

Consul是Go语言写的开源 的服务器发现软件,用于实现 分布式系统的服务发现与配置,包含 多个组件,作为一个整体,它为基础设施提供服务发现和服务配置的工具,提供以下关键特性:
  • 服务发现:consul通过DNS或者HTTP接口使服务注册服务发现变的很容易,一些外部服务,例如saas提供的也可以一样注册,consul采用Raft一致性协议算法来保证服务的高可用,使用GOSSIP协议管理成员广播消息

  • 健康检查:健康检测使consul可以快速的告警在集群中的操作,和服务发现的集成,可以防止服务转发到故障的服务上面(心跳机制)

  • 键/值存储:一个用来存储动态配置的系统,提供简单的HTTP接口,可以在任何地方操作

  • 多数据中心方案支持:无需复杂的配置,即可支持任意数量的区域

  • 简易安装:安装包仅包含一个二进制文件,支持跨平台,可与Docker等轻量级容器实现无缝对接

  • 提供Web管理界面:官方提供web管理界面

官方建议:最好是三台或者三台以上的consul在运行,同名服务最好是三台或三台以上,默认可
以搭建集群

官网:https://www.consul.io/

Git地址:https://github.com/hashicorp/consul

  1. consul安装

Consul用Golang实现,因此具有天然可移植性 (支持 Linux、windows和macOS),安装包仅包含一个可执行文件, Consul安装非常简单,只需要下载对应系统的软件包并解压后就可使用

(1). windows电脑安装consul

1).下载consul

下载地址: https://www.consul.io/downloads

2).解压consul到一个目录

把consul解压到E盘

3).配置consul到环境变量

a.电脑左下方设置选项
b.选择系统
c.关于->高级系统设置
d.选择'环境变量'
e.配置consul到环境变量

4).测试consul是否安装成功

重启计算机,命令行运行 consul -v, 出现如下代码,说明安装成功

(2).Mac电脑安装consul

方法一

运行以下命令即可
brew tap hashicorp/tap
brew install hashicorp/tap/consul

方法二

和windows类似
1).下载consul
2).减压consul到一个目录 配置consul到环境变量
或者下载解压后,将文件放置在 /usr/local/bin 目录下
3).查看安装情况
consul --version

(3).Linux电脑安装consul

方法一

Ubuntu安装步骤
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com
$(lsb_release -cs) main"
sudo apt-get update && sudo apt-get install consul
CentOS安装步骤
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo
https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo yum -y install consul

方法二

和windows/mac安装类似
1).下载consul
2).减压consul到一个目录 配置consul到环境变量
[root@localhost ~]# mkdir -p usr/local/consul
[root@localhost ~]# unzip consul_1.11.4_linux_arm64.zip -d /usr/local/consul/
[root@localhost ~]# cd /usr/local/consul/
[root@localhost ~]# ./consul -v
如果要全局使用请把consul加入到环境变量,或者下载解压后,将文件放置在 /usr/local/bin 目录下
3).安装验证
安装 Consul后,通过执行 consul命令,可以看到命令列表的输出
ok,consul在各个环境安装完成

二. consul的使用

  1. consul的角色

完成consul的安装后,必须运行 agent, agent可以运行为 server模式client模式或者 dev模式, 每个数据中心至少必须拥 有一台serve,建议在一个集群中有 3或者5个server,因为部署单一server,在出现失败时,也许会不可避免的出现数据丢失
  • client客户端角色: 将 HTTP 和 DNS 接口请求转发给局域网内的Server服务端集群

  • server服务端角色 :保存配置信息、实现高可用集群、在局域网内与本地客户端通讯、通过广域网与其他数据中心通讯等, 每个数据中心(集群)的 server 数量推荐为3个或是5个

  • 开发模式:主要用于开发阶段(dev模式也是server模式)

consul安装好之后,在使用之前,先了解一下consul都有哪些命令,使用命令 consul -h可以查看consul支持的所有参数,而且每个参数里面还支持其他参数
C:\Users\zhoupenghui>consul -h
Usage: consul [--version] [--help] <command> [<args>]

Available commands are:
    acl             Interact with Consul's ACLs
    agent           Runs a Consul agent
    catalog         Interact with the catalog
    config          Interact with Consul's Centralized Configurations
    connect         Interact with Consul Connect
    debug           Records a debugging archive for operators
    event           Fire a new event
    exec            Executes a command on Consul nodes
    force-leave     Forces a member of the cluster to enter the "left" state
    info            Provides debugging information for operators.
    intention       Interact with Connect service intentions
    join            Tell Consul agent to join cluster
    keygen          Generates a new encryption key
    keyring         Manages gossip layer encryption keys
    kv              Interact with the key-value store
    leave           Gracefully leaves the Consul cluster and shuts down
    lock            Execute a command holding a lock
    login           Login to Consul using an auth method
    logout          Destroy a Consul token created with login
    maint           Controls node or service maintenance mode
    members         Lists the members of a Consul cluster
    monitor         Stream logs from a Consul agent
    operator        Provides cluster-level tools for Consul operators
    peering         Create and manage peering connections between Consul clusters
    reload          Triggers the agent to reload configuration files
    rtt             Estimates network round trip time between nodes
    services        Interact with services
    snapshot        Saves, restores and inspects snapshots of Consul server state
    tls             Builtin helpers for creating CAs and certificates
    troubleshoot    CLI tools for troubleshooting Consul service mesh
    validate        Validate config files/directories
    version         Prints the Consul version
    watch           Watch for changes in Consul
agent参数说明:指令是consul的核心,它运行agent来维护成员的重要信息、运行检查、服务宣布、查询处理等等
  1. consul agent -dev 开发者模式启动consul

开发阶段可以使用下面命令启动consul ,执行 consul agent -dev 启动了一个consul 服务端
consul agent -dev
访问 http://localhost:8500 可以打开Web管理界面,如下:
  1. consul和gRPC结合使用

(1).启动consul

代码中结合gRPC和consul使用时,需要先 启动consul ,开发阶段通过 consul agent -dev 启动consul,在操作consul时使用的是 github.com/hashicorp/consul 这个包,需要先下载,可以通过命令 go get -u -v github.com/hashicorp/consul,或者在import中引入 github.com/hashicorp/consul/api后,使用go mod tidy来加载

(2).注册一个服务到consul上

把上一节中的 hello grpc服务注册到consul上,找到server/hello/main.go,编辑,代码如下:
增加了 consul服务相关代码,其它地方保存不变
package main

import (
    "context"
    "fmt"
    "net"
    "google.golang.org/grpc"
    "github.com/hashicorp/consul/api"
    "serverHello/proto/helloService"
)

//rpc远程调用的接口,需要实现hello.proto中定义的Hello服务接口,以及里面的方法
//1.定义远程调用的结构体和方法,这个结构体需要实现HelloServer的接口

type Hello struct{}

//SayHello方法参考hello.pb.go中的接口
/*
// HelloServer is the server API for Hello service.
type HelloServer interface {
    // 通过rpc来指定远程调用的方法:
    // SayHello方法, 这个方法里面实现对传入的参数HelloReq, 以及返回的参数HelloRes进行约束
    SayHello(context.Context, *HelloReq) (*HelloRes, error)
}
 */
func (this Hello) SayHello(c context.Context, req *helloService.HelloReq) (*helloService.HelloRes, error) {
    fmt.Println(req)
    return &helloService.HelloRes{
        Message: "你好" + req.Name,
    }, nil
}

func main() {
    //------------------------- consul服务相关----------------------
    //注册consul服务
    //1、初始化consul配置
    consulConfig := api.DefaultConfig()
    //设置consul服务器地址: 默认127.0.0.1:8500, 如果consul部署到其它服务器上,则填写其它服务器地址
    //consulConfig.Address = "127.0.0.1:8500"
    //2、获取consul操作对象
    consulClient, _ := api.NewClient(consulConfig)
    // 3、配置注册服务的参数
    agentService := api.AgentServiceRegistration{
        ID:      "1",  // 服务id,顺序填写即可
        Tags:    []string{"test"},  // tag标签
        Name:    "HelloService",  //服务名称, 注册到服务发现(consul)的K
        Port:    8080, // 端口号: 需要与下面的监听, 指定 IP、port一致
        Address: "127.0.0.1",  // 当前微服务部署地址: 结合Port在consul设置为V: 需要与下面的监听, 指定 IP、port一致
        Check: &api.AgentServiceCheck{  //健康检测
            TCP:      "127.0.0.1:8080",  //前微服务部署地址,端口 : 需要与下面的监听, 指定 IP、port一致
            Timeout:  "5s",  // 超时时间
            Interval: "30s",  // 循环检测间隔时间
        },
    }

    //4、注册服务到consul上
    consulClient.Agent().ServiceRegister(&agentService)

    //------------------------- grpc相关----------------------
    //1. 初始一个 grpc 对象
    grpcServer := grpc.NewServer()
    //2. 注册服务
    //helloService.RegisterHelloServer(grpcServer, &Hello{})
    // &Hello{}和 new(Hello)相同
    helloService.RegisterHelloServer(grpcServer, new(Hello))
    //3. 设置监听, 指定 IP、port
    listener, err := net.Listen("tcp", "127.0.0.1:8080")
    if err != nil {
        fmt.Println(err)
    }
    // 4退出关闭监听
    defer listener.Close()
    //5、启动服务
    grpcServer.Serve(listener)
}

效果展示:

运行该main..go
consul界面展示效果如下:

说明HelloService微服务注册到了consul这个服务发现中了

(3).在客户端使用服务发现获取hello微服务相关

把上一节中的 hello grpc服务客户端代码修改一下,通过consul获取hello微服务相关,找到server/client/main.go,编辑,增加了 consul服务相关代码,在连接服务器的时候,使用consul返回的微服务地址, 其它地方保存不变,代码如下:
package main

import (
    "client/proto/helloService"
    "context"
    "fmt"
    "google.golang.org/grpc"
    "github.com/hashicorp/consul/api"
    "google.golang.org/grpc/credentials/insecure"
    "strconv"
)

func main() {
    //----------------------------consul相关---------------------------
    //初始化consul配置, 客户端服务器需要一致
    consulConfig := api.DefaultConfig()
    //设置consul服务器地址: 默认127.0.0.1:8500, 如果consul部署到其它服务器上,则填写其它服务器地址
    //consulConfig.Address = "127.0.0.1:8500"
    //2、获取consul操作对象
    consulClient, _ := api.NewClient(consulConfig)  //目前先屏蔽error,也可以获取error进行错误处理
    //3、获取consul服务发现地址,返回的ServiceEntry是一个结构体数组
    //参数说明:service:服务名称,服务端设置的那个Name, tag:标签,服务端设置的那个Tags,, passingOnly bool, q: 参数
    serviceEntry, _, _ := consulClient.Health().Service("HelloService", "test", false, nil)
    //打印地址
    fmt.Println(serviceEntry[0].Service.Address)
    fmt.Println(serviceEntry[0].Service.Port)
    //拼接地址
    //strconv.Itoa: int转string型
    address := serviceEntry[0].Service.Address + ":" + strconv.Itoa(serviceEntry[0].Service.Port)

    //----------------------------hello微服务相关------------------------------
    // 1、连接服务器
    /*
        credentials.NewClientTLSFromFile :从输入的证书文件中为客户端构造TLS凭证。
        grpc.WithTransportCredentials :配置连接级别的安全凭证(例如,TLS/SSL),返回一个
        DialOption,用于连接服务器。
    */
    //把上面拼接的地址放入下面
    grpcClient, err := grpc.Dial(address, grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        fmt.Println(err)
    }

    //2、注册客户端
    client := helloService.NewHelloClient(grpcClient)
    //3、调用服务端函数, 实现HelloClient接口:SayHello()
    /*
        // HelloClient is the client API for Hello service.
        //
        // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
        type HelloClient interface {
            // 通过rpc来指定远程调用的方法:
            // SayHello方法, 这个方法里面实现对传入的参数HelloReq, 以及返回的参数HelloRes进行约束
            SayHello(ctx context.Context, in *HelloReq, opts ...grpc.CallOption) (*HelloRes, error)
        }
    */
    res, err1 := client.SayHello(context.Background(), &helloService.HelloReq{
        Name: "张三",
    })
    if err1 != nil {
        fmt.Printf("调用服务端代码失败: %s", err1)
        return
    }

    fmt.Printf("%#v\r\n", res)
    fmt.Printf("调用成功: %s", res.Message)
}

(4).校验客户端-服务发现-服务端功能是否成功

启动consul

见上面操作

启动服务端

见上面操作

启动客户端

校验成功

(5).测试不同服务端口下,客户端调用hello微服务操作

修改服务端hello微服务main.go中的端口号,改为8081,然后重新运行该微服务,再使用客户端请求服务发现,看看返回的hello微服务对应的端口号发生变化没有,如果发生变化了,说明操作成功,以此为案例,开发者可以把微服务服务端部署到不同服务器上,并指定对应的端口,从而实现微服务的负载均衡操作

F:\www\go-data\src\go_code\micro\grpc_demo\server\hello>go run .\main.go

name:"张三"

(6)实现goods微服务-服务发现-客户端操作

把上一节 goods微服务注册到consul上,找到server/goods/main.go,编辑,代码如下:
增加了 consul服务相关代码,其它地方保存不变
package main

import (
    "context"
    "fmt"
    "github.com/hashicorp/consul/api"
    "goods/proto/goodsService"
    "net"
    "google.golang.org/grpc"
    "strconv"
)

//rpc远程调用的接口,需要实现goods.proto中定义的Goods服务接口,以及里面的方法
//1.定义远程调用的结构体和方法,这个结构体需要实现GoodsServer的接口

type Goods struct{}

//GoodsServer方法参考goods.pb.go中的接口
/*
// GoodsServer is the server API for Goods service.
type GoodsServer interface {
    // 通过rpc来指定远程调用的方法:
    // AddGoods方法:增加商品, 这个方法里面实现对传入的参数AddGoodsReq, 以及返回的参数AddGoodsRes进行约束
    AddGoods(context.Context, *AddGoodsReq) (*AddGoodsRes, error)
    // 获取商品列表: GetGoodsReq 参数可为空, 返回参数GetGoodsRes是一个商品相关的切片
    GetGoods(context.Context, *GetGoodsReq) (*GetGoodsRes, error)
}
*/
//增加商品数据
func (this Goods) AddGoods(c context.Context, req *goodsService.AddGoodsReq) (*goodsService.AddGoodsRes, error) {
    fmt.Println(req)
    //模拟返回操作,正式项目在这里进行数据库的操作即可,根据操作结果,返回相关数据
    return &goodsService.AddGoodsRes{
        Message: "增加数据成功",
        Success: true,
    }, nil
}

//获取商品列表
func (g Goods) GetGoods(c context.Context, req *goodsService.GetGoodsReq) (*goodsService.GetGoodsRes, error) {
    //  GoodsList []*GoodsModel
    var tempList []*goodsService.GoodsModel  //定义返回的商品列表切片
    //模拟从数据库中获取商品的请求,循环结果,把商品相关数据放入tempList切片中
    for i := 0; i < 10; i++ {
        tempList = append(tempList, &goodsService.GoodsModel{
            Title:   "商品" + strconv.Itoa(i),  // strconv.Itoa(i): 整型转字符串类型
            Price:   float64(i),  //float64(i): 强制转换整型为浮点型
            Content: "测试商品内容" + strconv.Itoa(i),
        })
    }
    return &goodsService.GetGoodsRes{
        GoodsList: tempList,
    }, nil
}

func main() {
    //------------------------- consul服务相关----------------------
    //注册consul服务
    //1、初始化consul配置
    consulConfig := api.DefaultConfig()
    //设置consul服务器地址: 默认127.0.0.1:8500, 如果consul部署到其它服务器上,则填写其它服务器地址
    //consulConfig.Address = "127.0.0.1:8500"
    //2、获取consul操作对象
    consulClient, _ := api.NewClient(consulConfig)
    // 3、配置注册服务的参数
    agentService := api.AgentServiceRegistration{
        ID:      "1",  // 服务id,顺序填写即可
        Tags:    []string{"test"},  // tag标签
        Name:    "GoodsService",  //服务名称, 注册到服务发现(consul)的K
        Port:    8080, // 端口号: 需要与下面的监听, 指定 IP、port一致
        Address: "127.0.0.1",  // 当前微服务部署地址: 结合Port在consul设置为V: 需要与下面的监听, 指定 IP、port一致
        Check: &api.AgentServiceCheck{  //健康检测
            TCP:      "127.0.0.1:8080",  //前微服务部署地址,端口 : 需要与下面的监听, 指定 IP、port一致
            Timeout:  "5s",  // 超时时间
            Interval: "30s",  // 循环检测间隔时间
        },
    }

    //4、注册服务到consul上
    consulClient.Agent().ServiceRegister(&agentService)

    //1. 初始一个 grpc 对象
    grpcServer := grpc.NewServer()
    //2. 注册服务
    //helloService.RegisterGoodsServer(grpcServer, &Goods{})
    // &Hello{}和 new(Hello)相同
    goodsService.RegisterGoodsServer(grpcServer, new(Goods))
    //3. 设置监听, 指定 IP、port
    listener, err := net.Listen("tcp", "127.0.0.1:8080")
    if err != nil {
        fmt.Println(err)
    }
    // 4退出关闭监听
    defer listener.Close()
    //5、启动服务
    grpcServer.Serve(listener)
}

效果展示:

运行该main..go
consul界面展示效果如下:

说明GoodsService微服务注册到了consul这个服务发现中了

(7).在客户端使用服务发现获取goods微服务相关

把上一节中的 goods grpc服务注册客户端代码修改一下,通过consul获取goods微服务相关,找到server/goods/main.go,编辑,增加了 consul服务相关代码,在连接服务器的时候,使用consul返回的微服务地址, 其它地方保存不变,代码如下:
package main

import (
    "client/proto/goodsService"
    "context"
    "fmt"
    "github.com/hashicorp/consul/api"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
    "strconv"
)

func main() {
    //----------------------------consul相关---------------------------
    //初始化consul配置, 客户端服务器需要一致
    consulConfig := api.DefaultConfig()
    //设置consul服务器地址: 默认127.0.0.1:8500, 如果consul部署到其它服务器上,则填写其它服务器地址
    //consulConfig.Address = "127.0.0.1:8500"
    //2、获取consul操作对象
    consulClient, _ := api.NewClient(consulConfig)  //目前先屏蔽error,也可以获取error进行错误处理

    //3、获取consul服务发现地址,返回的ServiceEntry是一个结构体数组
    //参数说明:service:服务名称,服务端设置的那个Name, tag:标签,服务端设置的那个Tags,, passingOnly bool, q: 参数
    //serviceEntry, _, _ := consulClient.Health().Service("HelloService", "test", false, nil)
    //打印地址
    //fmt.Println(serviceEntry[0].Service.Address)
    //fmt.Println(serviceEntry[0].Service.Port)
    //拼接地址
    //strconv.Itoa: int转string型
    //address := serviceEntry[0].Service.Address + ":" + strconv.Itoa(serviceEntry[0].Service.Port)

    //----------------------------hello微服务相关------------------------------
    // 1、连接服务器
    /*
        credentials.NewClientTLSFromFile :从输入的证书文件中为客户端构造TLS凭证。
        grpc.WithTransportCredentials :配置连接级别的安全凭证(例如,TLS/SSL),返回一个
        DialOption,用于连接服务器。
    */
    //把上面拼接的地址放入下面
    //grpcClient, err := grpc.Dial(address, grpc.WithTransportCredentials(insecure.NewCredentials()))
    //if err != nil {
    //    fmt.Println(err)
    //}

    //2、注册客户端
    //client := helloService.NewHelloClient(grpcClient)
    //3、调用服务端函数, 实现HelloClient接口:SayHello()
    /*
        // HelloClient is the client API for Hello service.
        //
        // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
        type HelloClient interface {
            // 通过rpc来指定远程调用的方法:
            // SayHello方法, 这个方法里面实现对传入的参数HelloReq, 以及返回的参数HelloRes进行约束
            SayHello(ctx context.Context, in *HelloReq, opts ...grpc.CallOption) (*HelloRes, error)
        }
    */
    //res, err1 := client.SayHello(context.Background(), &helloService.HelloReq{
    //    Name: "张三",
    //})
    //if err1 != nil {
    //    fmt.Printf("调用服务端代码失败: %s", err1)
    //    return
    //}
    //
    //fmt.Printf("%#v\r\n", res)
    //fmt.Printf("调用成功: %s", res.Message)


    ----------------------------goods微服务相关--------------------------

    //3、获取consul服务发现地址,返回的ServiceEntry是一个结构体数组
    //参数说明:service:服务名称,服务端设置的那个Name, tag:标签,服务端设置的那个Tags,, passingOnly bool, q: 参数
    serviceGoodsEntry, _, _ := consulClient.Health().Service("GoodsService", "test", false, nil)
    //打印地址
    fmt.Println(serviceGoodsEntry[0].Service.Address)
    fmt.Println(serviceGoodsEntry[0].Service.Port)
    //拼接地址
    //strconv.Itoa: int转string型
    addressGoods := serviceGoodsEntry[0].Service.Address + ":" + strconv.Itoa(serviceGoodsEntry[0].Service.Port)

    // 1、连接服务器
    /*
        credentials.NewClientTLSFromFile :从输入的证书文件中为客户端构造TLS凭证。
        grpc.WithTransportCredentials :配置连接级别的安全凭证(例如,TLS/SSL),返回一个
        DialOption,用于连接服务器。

    */
    grpcGoodsClient, err := grpc.Dial(addressGoods, grpc.WithTransportCredentials(insecure.NewCredentials()))

    if err != nil {
        fmt.Println(err)
    }
    //2、注册客户端
    clientGoods := goodsService.NewGoodsClient(grpcGoodsClient)
    //增加
    res1, _ := clientGoods.AddGoods(context.Background(), &goodsService.AddGoodsReq{
        Goods: &goodsService.GoodsModel{
            Title:   "测试商品",
            Price:   20,
            Content: "测试商品的内容",
        },
    })
    fmt.Println(res1.Message)
    fmt.Println(res1.Success)

    //获取商品数据
    res2, _ := clientGoods.GetGoods(context.Background(), &goodsService.GetGoodsReq{})
    fmt.Printf("%#v", res2.GoodsList)

    for i := 0; i < len(res2.GoodsList); i++ {
        fmt.Printf("%#v\r\n", res2.GoodsList[i])
    }
}

(8).校验客户端-服务发现-服务端功能是否成功

启动consul

见上面操作

启动服务端

见上面操作

启动客户端

校验成功

(9).注销服务

func main(){
    //初始化consul配置,客户端服务器需要一致
    consulConfig := api.DefaultConfig()
    //获取consul操作对象
    registerClient,_ := api.NewClient(consulConfig)
    //注销服务ServiceDeregister(ServerID),ServerID: 微服务服务端服务发现id
    registerClient.Agent().ServiceDeregister("1")
}

.consul集群,监控检查,服务发现

通过上面例子已经可以实现 注册服务发现服务了,下面给大家详细的讲解一下consul中的 服务端以及 客户端
  1. 简介

Consul 是 HashiCorp 公司推出的开源工具,用于实现 分布式系统的服务发现与配置,Consul 是 分布式的高可用的可横向扩展的,完成consul的安装后,必须运行 agent代理, agent可以运行为 server模式client模式
  • 服务模式(server模式): 主要参与维护集群状态响应RPC查询,与其他数据中心交换WAN gossip ,以及向上级或远程数据中心转发查询,并且会将数据持久化,推荐使用3到5台机器

  • 客户模式(client模式):客户模式下ConsulAgent是一个非常轻量级的进程,它消耗最小的资源开销和少量的网络带宽,提供注册服务运行健康检查,并将查询转发给服务器,客户端是相对无状态的,客户端执行的唯一后台活动是LANgossip池,不负责数据的持久化,客户模式不能单独存在,必须要有一个服务模式的Consul

  • 数据中心:一个数据中心由多个ServerClient模式Consul组成,多个数据中心通过WAN通信,每个数据中心(数据中心是一个大型的计算机系统集群,通常由成千上万台计算机、存储设备、网络设备、电源设备、冷却设备等组成,以提供高效、可靠的计算和存储能力,主要功能是提供云计算、虚拟化、存储和数据处理等服务,以满足企业、政府机构和个人的计算和数据存储需求,是现代企业和政府机构不可或缺的基础设施之一)至少必须拥有一台server,建议在一个集群中有3或者5个server,部署单一server,在出现失败时,会不可避免的出现数据丢失

图片上 datacenter 分成上下两个部分, 但是这两个部分又不是完全隔离的,他们之间通过 WAN GOSSIP 进行报文交互,单个 datacenter 中,节点被划分成两种颜色, 红色的 server, 紫色的 client, 他们之间通过 GRPC 进行通信(业务数据), 除此之外, Client 和 Server 之间通过还有一条 LAN Gosssip 进行通信,比如,当 Server 节点增加,或者 down 机后,Client 可以获取对应的 Server列表,去除或者增加 Server 列表,同一个 Consul agent 程序,启动的时候,通过制定不同的参数来运行 Server 和 Client 模式,也就是说 client 和 server 本质上都是 Client Agent
一个client是一个非常 轻量级的进程,用于 注册服务,运行 健康检查转发对server的查询,每个数据中心 至少必须拥有一个server,agent必须在集群中的每个主机上运行,
注意:
server也可以用于注册服务,比如前面运行的 consul agent -dev ,但是正式上线后一般 通过client注册服务,使用保存配置信息、实现高可用集群、通过广域网与其他数据中心通讯等

Server功能

  • 参与共识仲裁(raft)

  • 存储机器状态(日志存储)

  • 处理查询

  • 维护周边(LAN/WAN) 节点之间的关系

Client功能

  • 负责通过该节点注册到 Consul 微服务的健康检查

  • 将客户端的注册请求和查询转换为 server 的 RPC 请求

  • 维护周边各节点(LAN/WAN) 的关系

  1. Client-Server 模式

Consul 的架构升级为 client-Server 模式,服务注册不再向 Consul-Server进行注册,而是向服务所在机器的 Consul-client 进行注册,通过 Consul-Client 将注册信息同步 Consul-Server 机器中

  1. 纯 Server 模式

Zookeeper 就是这种模,client server 本质上都是 consul 的 agent, 只是 角色不同,架构图如下:
  1. 架构的问题

纯 server 模式架构的问题

高可用服务实例注册时配置的 consul-host 是负载均衡地址,服务注册到集群后,由集群中的一个节点负责对该实例进行健康检查,假如有这样的情况:服务A,服务B,都注册到 Service1 ,也就是由 Service1 对 服务A,服务B 进行健康检查,当 Service1 宕机时,这样会导致 服务A,服务B 在注册列表中消失,导致无法无法访问到,但是其实服务本身没有问题。服务注销:当服务从注册中心注销时,由于是负载均衡的,可能会导致服务注销失败,因为要在Service1 上注销,有可能负载均衡到 Service2 上进行注销,导致注销失败,解决的办法:遍历集群中所有节点进行注销

Client-Server 架构的问题

高可用服务实例向本级别 consul-client 进行注册,如果 consul-client 不可用,只影响 consul-client 所在机器的服务实例,不影响另外一台机器上的实例服务注销服务注销值需要在 consul-client 上进行注销,不需要经过负载均衡

  1. 基于 Client-Server架构服务注册时序图

服务端口

  • 8300: 只存在于Server模式,选取Leader节点(Raft协议),为Leader节点和Client节点的提供RPC调用

  • 8301: LAN网中集群数据同步的通信端口(Gossip协议),也是加入集群的通信端口

  • 8302: 只存在于Server模式,WAN网中集群数据同步的通信端口(Gossip协议),也是加入集群的通信端口,主要支持数据中心与数据中心之间交互通过WLAN(8302端口)

  • 8500: 提供Http服务(或web界面)

  • 8600: 提供DNS服务端口

8301和8302接口作用类似,主要区分在于8301用于LAN网络,8302用于WAN网络,它们都可以用于加入consul集群(数据中心一致就是在一个集群),将各数据中心连接则使用8302端口

实现原理

核心原理在于亮点:
  • 集群信息之间的高效同步机制,保障拓扑变动与控制信号的及时同步

  • server 集群内日志存储强一致性

他们主要基于两个协议来实现:
  • Gossip 协议,在集群内消息传递

  • 使用 raft 协议保障日志的一致性

案例讲解

架构图

步骤

(1).部署集群
首先需要有一个正常的Consul集群,有Server,有 Leader(主服务领导),这里在服务器Server1、Server2、Server3上分别部署了Consul Server
(2).选举Leader节点
假设选举了Server2上的 Consul Server 节点为Leader,这些服务器上最好只部署Consul程序,以尽量维护Consul Server的稳定
(3).注册服务
然后在服务器Server5和Server7上通过Consul Client分别注册Service A、B、C,这里每个Service 分别部署在了 两个服务器上,这样可以避免Service的 单点问题,服务注册到Consul可以通过 HTTP API(8500 端口)的方式,也可以通过 Consul 配置文件的方式
(4).Consul Client转发注册消息
Consul Client 可以认为是无状态的,它将注册信息通过 RPC转发到Consul Server,服务信息保存在Server的各个节点中,并且通过 Raft实现了 强一致性
(5).服务发起通信请求
最后在服务器Server6中Service D需要访问Service B,这时候Service D首先访问本机Consul Client提供的HTTP API,本机Client会将请求转发到 Consul Server,Consul Server查询到Service B当前的信息返回,最终Service D拿到了Service B的所有部署的IP和端口,然后就可以选择Service B的其中一个部署并向其发起请求了
  1. 集群配置

准备四个虚拟机,安装好consul等相关软件(安装见上面讲解),三个虚拟机作为服务端(ip参考:192.168.1.129,192.168.1.130,192.168.1.131,),一个作为客户端(ip参考:192.168.1.132), 虚拟机查询consul如下:

其他虚拟机也是一致的,进行集群配置测试操作后

(1).启动服务端

启动服务端命令如下:

$ consul agent -server -bootstrap-expect 3 -node=server_01 -
bind=192.168.1.129 -ui -data-dir=/root/usr/local/consul/data -client 0.0.0.0

$ consul agent -server -bootstrap-expect 3 -node=server_02 -
bind=192.168.1.130 -ui -data-dir=/root/usr/local/consul/data -client 0.0.0.0

$ consul agent -server -bootstrap-expect 3 -node=server_03 -
bind=192.168.1.131 -ui -data-dir=/root/usr/local/consul/data -client 0.0.0.0

#额外参数:
-advertise=106.52.1.126 -datacenter=myDataCenter-CentOS  -enable-script-checks=true -config-dir=/etc/consul.d/consul.d -server-port=8300 -serf-lan-port=8301 -serf-wan-port=8302 -http-port=8500 -dns-port=8600 
需要先在/etc/下面创建consul.d目录,上面参数说明:
-server : 定义agent运行在 server模式,表示以Server模式启动,没有设置-server表示Client方式启动
-bootstrap-expect :在一个 datacenter(数据中心)中期望提供的server节点数目,当该值提供的时候, consul一直等到达到指定sever数目的时候才会引导整个集群,该标记不能和bootstrap,通俗来讲,就是 构成集群的最小数量
共用(注意:bootstrap-expect值必须是server的数量)
-bind :集群通信的ip,该地址用来在集群内部的通讯,集群内的所有节点到地址都必须是可达的,默认是0.0.0.0 ,表示所有ip
-node :节点在集群中的名称,在一个集群中必须是唯一的,默认是该节点的主机名
-ui : 启动web界面 :8500,启动后台管理,默认 http://ip:8500访问
-rejoin :使consul启动的时候加入集群中。
-config-dir :配置文件目录,里面所有以.json结尾的文件都会被加载
-client :consul服务侦听地址,这个地址提供HTTP、DNS、RPC等服务,默认是127.0.0.1所以不对外提供服务,如果要对外提供服务改成0.0.0.0, ,表示所有ip
data-dir :提供一个目录用来存放agent的状态,所有的agent允许都需要该目录,该目录
必须是稳定的,系统重启后都继续存在,通俗来讲就是 数据存放目录

额外参数
enable-script-checks=true: 允许使用脚本进行监控检查
advertise=IP: 告诉集群其他节点你通过这个ip来和我通信,默认使用bind绑定的ip
datacenter=dataCenterName: 数据中心名称
server-port=8300:选取Leader节点(raft协议通信)和提供RPC调用的
serf-lan-port=8301: 集群通信端口,用在LAN网
serf-wan-port=8302: 数据中心通信端口,用在WAN网
http-port=8500: 提供Http服务的端口
dns-port=8600: 提供Dns服务的端口

(2).启动客户端

运行cosnul agent以client模式
$ consul agent -data-dir=/root/usr/local/consul/data -node=client-01 -
bind=192.168.1.132 -ui -client 0.0.0.0

(3).关联集群

以server-01为Leader,分别在server-02、server-03、client-01节点上面运行下面命令建立集群关系
consul join 192.168.1.129
注意:分别 关闭对应服务器的防火墙(systemctl stop firewalld),或者允许对应端口

(4).具体操作

启动服务端

把下面命令分别在对应虚拟机上运行

上面三条命令分别在对应虚拟机运行成功后,再启动客户端

启动客户端

把下面命令分别在对应虚拟机上运行

操作完后,三台服务端,一台客户端就启动完成了,

关联

以server-01(192.168.1.129)为Leader,分别在server-02、server-03、client-01节点上面运行下面命令建立集群关系, consul join 192.168.1.129
consul join 192.168.1.129

这样以server-01为Leader的集群就创建好了,浏览器打开web ui,可以看见:在server-01的服务器上,关联了三个服务(2个服务端,1个客户端),图示如下:

好了,集群就搭建好了

(5).查看consul成员和集群状态

consul members
节点 网络地址 状态 类型 版本 协议 数据中心 分管部分
Node Address Status Type Build Protocol DC Partition Segment

  1. Consul集群实现微服务

以上面代码为案例,先注册服务到sever里面,把Hello微服务注册到server-03这个consul中,代码只需修改consulConfig.Address=192.138.1.132,以及Hello微服务启动的地址,比如把127.0.0.1修改为本机地址192.168.1.111,这样才能让server-03和微服务服务器通讯,Goods微服务也类似操作

注意:需要配置consul集群地址,其中 consulConfig.Address = "192.168.234.132:8500" 为集群客户端地址 AgentServiceRegistration 中的地址需要和当前程序运行服务器的地址统一起来
package main

import (
    "context"
    "fmt"
    "net"
    "google.golang.org/grpc"
    "github.com/hashicorp/consul/api"
    "serverHello/proto/helloService"
)

//rpc远程调用的接口,需要实现hello.proto中定义的Hello服务接口,以及里面的方法
//1.定义远程调用的结构体和方法,这个结构体需要实现HelloServer的接口

type Hello struct{}

//SayHello方法参考hello.pb.go中的接口
/*
// HelloServer is the server API for Hello service.
type HelloServer interface {
    // 通过rpc来指定远程调用的方法:
    // SayHello方法, 这个方法里面实现对传入的参数HelloReq, 以及返回的参数HelloRes进行约束
    SayHello(context.Context, *HelloReq) (*HelloRes, error)
}
 */
func (this Hello) SayHello(c context.Context, req *helloService.HelloReq) (*helloService.HelloRes, error) {
    fmt.Println(req)
    return &helloService.HelloRes{
        Message: "你好" + req.Name,
    }, nil
}

func main() {
    //------------------------- consul服务相关----------------------
    //注册consul服务
    //1、初始化consul配置
    consulConfig := api.DefaultConfig()
    //设置consul服务器地址: 默认127.0.0.1:8500, 如果consul部署到其它服务器上,则填写其它服务器地址 
    consulConfig.Address = "192.168.1.132:8500"
    //2、获取consul操作对象
    consulClient, _ := api.NewClient(consulConfig)
    // 3、配置注册服务的参数
    agentService := api.AgentServiceRegistration{
        ID:      "1",  // 服务id,顺序填写即可
        Tags:    []string{"test"},  // tag标签
        Name:    "HelloService",  //服务名称, 注册到服务发现(consul)的K
        Port:    8082, // 端口号: 需要与下面的监听, 指定 IP、port一致
        Address: "192.1678.1.111",  // 当前微服务部署地址: 结合Port在consul设置为V: 需要与下面的监听, 指定 IP、port一致
        Check: &api.AgentServiceCheck{  //健康检测
            TCP:      "192.1678.1.111:8082",  //前微服务部署地址,端口 : 需要与下面的监听, 指定 IP、port一致
            Timeout:  "5s",  // 超时时间
            Interval: "30s",  // 循环检测间隔时间
        },
    }

    //4、注册服务到consul上
    consulClient.Agent().ServiceRegister(&agentService)

    //------------------------- grpc相关----------------------
    //1. 初始一个 grpc 对象
    grpcServer := grpc.NewServer()
    //2. 注册服务
    //helloService.RegisterHelloServer(grpcServer, &Hello{})
    // &Hello{}和 new(Hello)相同
    helloService.RegisterHelloServer(grpcServer, new(Hello))
    //3. 设置监听, 指定 IP、port
    listener, err := net.Listen("tcp", "192.1678.1.111:8082")
    if err != nil {
        fmt.Println(err)
    }
    // 4退出关闭监听
    defer listener.Close()
    //5、启动服务
    grpcServer.Serve(listener)
}

修改完后,和上面启动微服务操作一致,启动完后,查看,如下:Leader上已经注册好了相关微服务了

然后修改微服务客服端代码,让consulConfig.Address = "192.168.1.132:8500"(一般连接的是consul客户端),这样就访问到consul的客户端client-01了,从客户端就可以获取微服务对应数据了

  1. 退出集群

可以使用 Ctrl-C 优雅的关闭Agent,中断Agent之后可以看到服务离开了集群并关闭,在退出中,Consul提醒其他集群成员,这个节点离开了,如果强行杀掉进程,集群的其他成员应该能检测到这个节点失效了,当一个成员离开,他的服务和检测也会从目录中移除,当一个成员失效了,他的健康状况被简单的标记为危险,但是不会从目录中移除,Consul会 自动尝试对失效的节点进行重连,允许他从某些网络条件下恢复过来,离开的节点则不会再继续联系,此外,如果一个agent作为一个服务器,一个优雅的离开是很重要的,可以避免引起潜在的可用性故障影响达成一致性协议
停止server-01 测试程序是否正常,在对应consul服务优雅的退出运行命令 consul leave即可

然后使用命令consul members在其他三台consul服务器上查看server-01是否停止

server-01停止了,在浏览器上,consul web server-01查询,发现打不开了

而在其他server consul web ui 上可以打开,并且Leader已经发生变化(因为server-01停止了), 这样就实现了负载均衡操作,客户端访问微服务就不会挂掉

上面讲解的是一个客户端的案例,当然也可以一个微服务对应一个客户端,这样当一个服务器挂掉的话,就只会影响一个微服务,其他的微服务不会受到影响

[上一节][golang 微服务] 4. gRPC介绍,Protobuf结合gRPC 创建微服务

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/629416.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

赋值运算符重载实例:Date类(结尾附源码)

赋值运算符重载实例&#xff1a;Date类 文章目录 赋值运算符重载实例&#xff1a;Date类一、构造日期类二、获取某年某月天数以及检查合法1.获取某年某月天数2.检查日期合法3.打印日期类 三、不同运算符的重载&#xff08;1&#xff09; ; !&#xff08;2&#xff09;> ; &g…

Lecture 13 Formal Language Theory Finite State Automata

目录 什么是语言&#xff1f;Formal Language Theory 形式语言理论动机例子除了从属问题之外的问题Regular Languages 正则语言Finite State Acceptor 正则语言的性质Derivational MorphologyWeighted FSAFinite State Transducer (FST)FST for Inflectional MorphologyNon-Reg…

Java Web实现用户登录功能

文章目录 一、纯JSP方式实现用户登录功能&#xff08;一&#xff09;实现思路1、创建Web项目2、创建登录页面3、创建登录处理页面4、创建登录成功页面5、创建登录失败页面6、编辑项目首页 &#xff08;三&#xff09;测试结果 二、JSPServlet方式实现用户登录功能&#xff08;一…

软考A计划-系统架构师-官方考试指定教程-(11/15)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…

第四十周周报

学习目标&#xff1a; 一、Stable Diffusion 论文 学习时间&#xff1a; 2023.6.3-2023.6.9 学习产出&#xff1a; 一、模型进展 相比前两周的结果&#xff0c;本周改进了一下参数&#xff0c;FID达到了9 前两周结果为10.258 本周相比前两周FID降低1 二、High-Resolu…

数据结构之树与二叉树——算法与数据结构入门笔记(五)

本文是算法与数据结构的学习笔记第五篇&#xff0c;将持续更新&#xff0c;欢迎小伙伴们阅读学习。有不懂的或错误的地方&#xff0c;欢迎交流 引言 前面章节介绍的都是线性存储的数据结构&#xff0c;包括数组、链表、栈、队列。本节带大家学习一种非线性存储的数据结构&…

【MySQL】- 05 sql 语句练习题

sql 语句练习题 一 测试数据的准备二、数据查询1、查询"01"课程比"02"课程成绩高的学生的信息及课程分数2、查询"01"课程比"02"课程成绩低的学生的信息及课程分数3、查询平均成绩大于等于60分的同学的学生编号和学生姓名和平均成绩4、…

MySQL数据库基础 06

第六章 多表查询 1. 一个案例引发的多表连接1.1 案例说明1.2 笛卡尔积&#xff08;或交叉连接&#xff09;的理解1.3 案例分析与问题解决 2. 多表查询分类讲解分类1&#xff1a;等值连接 vs 非等值连接等值连接非等值连接 分类2&#xff1a;自连接 vs 非自连接分类3&#xff1a…

动态网站开发02:Java Web概述

文章目录 一、 XML基础&#xff08;一&#xff09;XML概述1、XML2、XML与HTML的比较 &#xff08;二&#xff09;XML语法1、XML文档的声明2、XML元素的定义3、XML属性的定义4、XML注释的定义5、XML文件示例 &#xff08;三&#xff09;DTD约束1、什么是XML约束2、什么是DTD约束…

MM32F3273G8P火龙果开发板MindSDK开发教程5 - Gcc编译环境的配置

MM32F3273G8P火龙果开发板MindSDK开发教程5 - Gcc编译环境的配置 1、准备工作 用了几天Keil后&#xff0c;实在用不习惯&#xff0c;只好回到macos系统中来搭建gcc的编译环境。但是百问网火龙果开发板自带的DAP-Link在pyocd中根本识别不到&#xff0c;所以烧录还需要重新购置…

MySQL数据库基础 07

第七章 单行函数 1. 函数的理解1.1 什么是函数1.2 不同DBMS函数的差异1.3 MySQL的内置函数及分类 2. 数值函数2.1 基本函数2.2 角度与弧度互换函数2.3 三角函数2.4 指数与对数2.5 进制间的转换 3. 字符串函数4. 日期和时间函数4.1 获取日期、时间 4.2 日期与时间戳的转换 4.3 获…

一个关于宏定义的问题,我和ChatGPT、NewBing、Google Bard、文心一言 居然全军覆没?

文章目录 一、问题重述二、AI 解题2.1 ChatGPT2.2 NewBing2.3 Google Bard2.4 文心一言2.5 小结 一、问题重述 今天在问答模块回答了一道问题&#xff0c;要睡觉的时候&#xff0c;又去看了一眼&#xff0c;发现回答错了。 问题描述&#xff1a;下面的z的值是多少。 #define…

【Redis】Redis持久化机制RDB与AOF

目录 一、RDB 1、概念 2、RDB文件保存 3、执行RDB 4、触发RDB 5、fork原理 6、RDB的缺点 二、AOF 1、概念 2、开启AOF 3、触发AOF 4、触发重写AOF 三、区别 一、RDB 1、概念 RDB全称为Redis Database Backup File&#xff08;Redis数据备份文件&#xff09;&…

提示工程师指南3-Prompt工程-高级提示

高阶Prompting 到这一步&#xff0c;应该很明显&#xff0c;改进提示有助于在不同任务上获得更好的结果。这就是Prompt工程背后的整个理念。 虽然之前的例子很有趣&#xff0c;但在我们深入了解更高级的概念之前&#xff0c;让我们先正式地介绍一些概念。 文章目录 高阶Promp…

中国机器元宇宙手术机器人市场迎来爆发期,思哲睿能否借势上市?

手术机器人作为一种能够辅助医生进行精准、微创的外科手术的高端医疗设备&#xff0c;近年来受到了国内外医疗界的广泛关注和重视。随着我国人口老龄化、医疗需求增加、医疗技术进步等因素的推动&#xff0c;手术机器人市场规模呈现快速增长态势。 在这样一个充满机遇和挑战的市…

docker部署prometheus+grafana视图监控

效果 一、grafana可视化平台部署 docker run -d \--namegrafana \--restartalways \-p 3000:3000 \grafana/grafanagrafana我也是部署在170.110服务器上&#xff0c;192.168.170.110:3000访问grafana 默认账号密码都是admin 二、部署exportor采集信息 针对各类数据库平台系统…

ASP.NET Core MVC 从入门到精通之Html辅助标签补充及模型校验基础

随着技术的发展&#xff0c;ASP.NET Core MVC也推出了好长时间&#xff0c;经过不断的版本更新迭代&#xff0c;已经越来越完善&#xff0c;本系列文章主要讲解ASP.NET Core MVC开发B/S系统过程中所涉及到的相关内容&#xff0c;适用于初学者&#xff0c;在校毕业生&#xff0c…

7. user-Agent破解反爬机制

文章目录 1. 为什么要设置反爬机制2. 服务器如何区分浏览器访问和爬虫访问3. 反爬虫机制4. User-Agent是什么5. 如何查询网页的User-Agent6. user-agent信息解析7. 爬虫程序user-agent和浏览器user-agent的区别8. 代码查看爬虫程序的user-agent9. 在代码中加入请求头信息 1. 为…

wkhtmltopdf踩坑记录

1. 不支持writing-mode。 需求是文字纵向排列&#xff0c;内容从左到右&#xff0c;本来用的是writing-mode: tb-rl;&#xff0c;插件转pdf后发现失效。 解决方法&#xff1a; 让每一列文字单独用一个div容器包裹&#xff0c;对它的宽度进行限制&#xff0c;控制每一行只能出现…

MySQL 搭建数据库表

创建MySQL数据表需要以下信息&#xff1a; 表名表字段名定义每个表字段 语法 以下为创建MySQL数据表的SQL通用语法&#xff1a; CREATE TABLE table_name (column_name column_type); 复制 以下例子中我们将在 RUNOOB 数据库中创建数据表runoob_tbl&#xff1a; CREATE …