golang使用etcd 服务注册与发现 测试可用

服务注册代码
复制代码
package main

import (
    "context"
    "fmt"
    "log"
    "net"
    "os"
    "os/signal"
    "time"

    "go.etcd.io/etcd/client/v3"
)

const (
    etcdEndpoints = "localhost:2379" // etcd地址
    serviceName   = "/services/my-service"     // 服务名称前缀
)

//空闲的端口号
func GetFreePort() (int, error) {
    addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:0")
    if err != nil {
        return 0, err
    }
    cli, err := net.ListenTCP("tcp", addr)
    if err != nil {
        return 0, err
    }
    defer cli.Close()
    return cli.Addr().(*net.TCPAddr).Port, nil
}
func registerService(client *clientv3.Client, service string, address string) error {
    // 创建一个租约
    resp, err := client.Grant(context.Background(), 2)
    if err != nil {
        return err
    }

    // 注册服务的键值对,将服务名称和地址写入etcd中
    _, err = client.Put(context.Background(), fmt.Sprintf("%s/%s", service, address), address, clientv3.WithLease(resp.ID))
    if err != nil {
        return err
    }

    // 定期刷新租约
    ch, err := client.KeepAlive(context.Background(), resp.ID)
    if err != nil {
        return err
    }

    go func() {
        for {
            select {
            case resp := <-ch:
                if resp == nil {
                    log.Println("KeepAlive channel closed")
                    return
                }
            }
        }
    }()

    return nil
}
func main() {
    // 创建etcd客户端
    client, err := clientv3.New(clientv3.Config{
        Endpoints:   []string{etcdEndpoints},
        DialTimeout: 5 * time.Second,
    })
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()
    port, err := GetFreePort()
    if err != nil {
        log.Fatal(err)
    }
    // 注册服务
    err = registerService(client, serviceName, fmt.Sprintf("localhost:%d", port))
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(fmt.Sprintf("服务注册成功:localhost:%d", port))
    sig := make(chan os.Signal, 1)
    signal.Notify(sig, os.Interrupt)
    select {
    case <-sig:
        fmt.Println("Program terminated.")
    }
}
复制代码

服务发现代码 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package main
 
import (
    "context"
    "fmt"
    "log"
    "os"
    "os/signal"
    "sync"
    "time"
 
    "go.etcd.io/etcd/client/v3"
)
 
const (
    etcdEndpoints = "localhost:2379"       // etcd地址
    serviceName   = "/services/my-service" // 服务名称前缀
)
 
type ServiceDiscovery struct {
    client     *clientv3.Client
    lock       sync.Mutex
    services   map[string]string
    watchedKey string
}
 
//创建服务对象
func NewServiceDiscovery(endpoints []string, watchedKey string) (*ServiceDiscovery, error) {
    client, err := clientv3.New(clientv3.Config{
        Endpoints: endpoints,
    })
    if err != nil {
        return nil, err
    }
 
    return &ServiceDiscovery{
        client:     client,
        lock:       sync.Mutex{},
        services:   make(map[string]string),
        watchedKey: watchedKey,
    }, nil
}
 
//服务发现
func (sd *ServiceDiscovery) DiscoverServices() error {
    resp, err := sd.client.Get(context.Background(), serviceName, clientv3.WithPrefix())
    if err != nil {
        return err
    }
    for _, kv := range resp.Kvs {
        fmt.Println(resp.Kvs)
        sd.lock.Lock()
        defer sd.lock.Unlock()
        sd.services[string(kv.Key)] = string(kv.Value)
    }
    return nil
}
 
//监听
func (sd *ServiceDiscovery) WatchServices() error {
    watchChan := sd.client.Watch(context.Background(), sd.watchedKey, clientv3.WithPrefix())
    for watchResp := range watchChan {
        for _, event := range watchResp.Events {
            switch event.Type {
            case clientv3.EventTypePut:
                sd.handlePutEvent(event)
            case clientv3.EventTypeDelete:
                sd.handleDeleteEvent(event)
            }
        }
    }
 
    return nil
}
 
//新增或者修改
func (sd *ServiceDiscovery) handlePutEvent(event *clientv3.Event) {
    sd.lock.Lock()
    defer sd.lock.Unlock()
 
    serviceName := string(event.Kv.Key)
    serviceAddress := string(event.Kv.Value)
 
    sd.services[serviceName] = serviceAddress
    log.Printf("Added service: %s -> %s\n", serviceName, serviceAddress)
}
 
//删除服务
func (sd *ServiceDiscovery) handleDeleteEvent(event *clientv3.Event) {
    sd.lock.Lock()
    defer sd.lock.Unlock()
 
    serviceName := string(event.Kv.Key)
 
    delete(sd.services, serviceName)
    log.Printf("Removed service: %s\n", serviceName)
}
 
func main() {
    serviceDiscovery, err := NewServiceDiscovery([]string{etcdEndpoints}, serviceName)
    if err != nil {
        log.Fatalf("Failed to create ServiceDiscovery: %v", err)
    }
    serviceDiscovery.DiscoverServices()
    go func() {
        err := serviceDiscovery.WatchServices()
        if err != nil {
            log.Fatalf("Failed to watch services: %v", err)
        }
    }()
    ticker := time.NewTicker(2 * time.Second)
    defer ticker.Stop()
    sig := make(chan os.Signal, 1)
    signal.Notify(sig, os.Interrupt)
    //打印服务的变化
    for {
        select {
        case <-ticker.C:
            fmt.Println("服务最新列表:")
            for _, v := range serviceDiscovery.services {
                fmt.Println(v)
            }
        case <-sig:
            fmt.Println("Program terminated.")
            return
        }
    }
}

  https://blog.csdn.net/weixin_46501427/article/details/133125020

posted @   fly_fly_fly#  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示