数据服务的分布式模型

 

数据服务的分布式模型

分布式系统要求个节点分布在网络上,并通过消息传递合作来完成一个共同的目标。
分布式系统的三大关键特征:

  • 节点之间并发工作
  • 没有全局锁
  • 某个节点发生错误不影响其它节点

模型

接口服务层提供对外的REST接口,而数据服务提供对接口服务的REST接口。
接口服务接收用户请求,数据服务接收接口服务的请求,接口服务作为HTTP客户端向数据服务请求数据。

1.每一个数据服务节点都需要向所有的接口服务通知自身存在,这里数据服务需要周期性向接口服务发送心跳包ip:port,而接口服务需要处理心跳包,维持可用的数据服务列表。

注意:心跳包,数据服务向apiServers此交换器发送信息,每个接口服务启动自动绑定此交换器,就可以接收到此交换器的信息。
2.接口服务接收到用户请求后,需要定位数据被保存在哪个数据服务节点上。

注意:定位,接口服务向dataServers此交换器发送信息,每个数据服务启动自动绑定此交换器,就可以结束此交换器的信息。

所以这里接口服务与数据服务是使用Rabbitmq消息队列通信。

 

 

 

 

数据服务data server

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
package main
 
import (
    "RabbitMQ"
    "fmt"
    "io"
    "log"
    "net/http"
    "os"
    "strconv"
    "strings"
    "time"
)
 
 
//Rabbitmq使用简介
//向交换机发送消息的步骤
//1.q := rabbitMQ.New("amqp://user:password@ip:port/","队列名")
//2.defer q.Close()
//3.q.Publish("交换机名","消息内容","")
 
//从exchange等待消息
//q := rabbitMQ.New("amqp://user:password@ip:port/","队列名")
//defer q.Close()
//q.Bind("交换机名","")
//c := q.Consume()
//for msg := range c {
////处理msg,msg为接收信息
//}
 
//向某消息队列发送消息
//q := rabbitMQ.New("amqp://user:password@ip:port/","队列名")
//defer q.Close()
//q.Send("队列名","Hello World!")
 
//接收消息队列的消息
//q := rabbitMQ.New("amqp://user:password@ip:port/","队列名")
//defer q.Close()
//c := q.Consume()
//msg := <-c
 
//注意
//数据封装和具体示例:https://www.cnblogs.com/-wenli/p/12203202.html
//2.发送消息,只需要连接到rabbitmq,就可以直接向某消息队列发送。
//3.接收消息,接收消息不管是从消息队列还是exchange,本质都是从信道中接收,信道默认为阻塞信道,玩过go语言的就很懂了!
 
 
 
//数据服务:
//1.每一个数据服务都必须向api exchange发送心跳信息ip:port。
//2.每一个数据服务启动时自动绑定dataSevers exchange,接受data exchange的定位消息,如果有资源,则返回自身地址ip:port,如果没有则不回复
func main() {
    //心跳服务
    go StartHeartbeat()
 
    //资源定位服务
    go StartLocate()
    http.HandleFunc("/objects/",Handler)
    println("server...")
    log.Fatal(http.ListenAndServe("127.0.0.1:8006", nil))
}
 
func Handler(w http.ResponseWriter, r  *http.Request){
    m := r.Method
    if m == http.MethodPut{
        Put(w,r)
        return
    }
    if m == http.MethodGet{
        Get(w,r)
        return
    }
    w.WriteHeader(http.StatusMethodNotAllowed)
}
 
func Put(w http.ResponseWriter,r *http.Request){
    //do something
}
 
 
func Get(w http.ResponseWriter,r *http.Request){
    //do something
}
 
 
//心跳服务
//1.向api exchange发送心跳信息
func StartHeartbeat(){
    q := rabbitMQ.New("rabbitmq链接","")
    defer q.Close()
    for{
         q.Publish("apiServers","127.0.0.1:8006","")
         time.Sleep(5 * time.Second)
    }
}
 
 
//资源定位服务
//2.接受data exchange的定位消息,如果有资源,则返回自身地址ip:port,如果没有则不回复
func  StartLocate(){
    q := rabbitMQ.New("rabbitmq链接","")
    defer q.Close()
    q.Bind("dataServers","")
    c := q.Consume()
    for msg := range c {
        object, e := strconv.Unquote(string(msg.Body))
        if e != nil {
            panic(e)
        }
        if Locate("D:/Go/test/"+ "/objects/" + object){
            fmt.Printf("%s is exist\n",object)
            q.Send(msg.ReplyTo,"127.0.0.1:8006")
        }else{
            fmt.Printf("%s is not exist\n",object)
            q.Send(msg.ReplyTo,"")
        }
    }
}
 
func Locate(name string)bool{
    _,err := os.Stat(name)
    return !os.IsNotExist(err)
}

 

接口服务 api Server

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
package main
 
import (
    "Heartbeat"
    "Locate"
    "fmt"
    "log"
    "net/http"
)
//接口服务
//1.每一个接口服务启动时自动绑定apiServers exchange,接收每一个数据服务的心跳包。
//2.当有客户端请求到某个接口服务时,此接口向dataServers exchange发送数据定位消息。
 
 
 
//服务端编写的业务逻辑处理程序
//hander函数: 具有func(w http.ResponseWriter, r *http.Requests)签名的函数
func myHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Println(r.RemoteAddr, "连接成功"//r.RemoteAddr远程网络地址
    fmt.Println("method = ", r.Method) //请求方法
    fmt.Println("url = ", r.URL.Path)
    fmt.Println("header = ", r.Header)
    fmt.Println("body = ", r.Body)
    w.Write([]byte("hello go")) //给客户端恢复的数据
}
 
 
func main() {
 
    //心跳服务
    //1.接收数据服务节点的心跳消息
    go Heartbeat.ListenHeartbeat()
 
    //2.服务端测试函数
    http.HandleFunc("/go", myHandler)
 
 
    //3.发送定位消息并接收定位消息
    http.HandleFunc("/locate/",Locate.Handler)
    println("server...")
    log.Fatal(http.ListenAndServe("127.0.0.1:8005", nil))
}

 

接口服务的心跳包处理heartbeat

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
package Heartbeat
 
import (
    "RabbitMQ"
    "fmt"
    "math/rand"
    "strconv"
    "sync"
    "time"
)
//接口服务的心跳包处理
//接口服务接收每一个数据服务的心跳包,并维持应该数据服务列表,并对数据服务列表进行数据保活机制。
 
var dataServers = make(map[string]time.Time)
var mutex sync.Mutex
 
//监听并处理数据服务的心跳包
func ListenHeartbeat(){
    q := rabbitMQ.New("rabbitmq链接","")
    defer q.Close()
    q.Bind("apiServers","")
    c := q.Consume()
    go removeExpiredDataServer()
    for msg := range c{
        dataServer,e := strconv.Unquote(string(msg.Body))
        if e != nil {
            panic(e)
        }
        mutex.Lock()  //加互斥锁
        fmt.Println(dataServer)
        dataServers[dataServer] = time.Now()
        mutex.Unlock() //解互斥锁
    }
}
 
func removeExpiredDataServer(){
    for{
        time.Sleep(5 * time.Second)
        mutex.Lock()
        for s,t := range dataServers {
            if t.Add(10*time.Second).Before(time.Now()){
                delete(dataServers,s)
            }
        }
        mutex.Unlock()
    }
}
 
//获得接口服务维持的数据服务列表
func GetDataServers() []string {
    mutex.Lock()
    defer mutex.Unlock()
    ds := make([]string,0)
    for s,_:= range dataServers {
        ds = append(ds,s)
    }
    return  ds
 
}
//随机选择一个数据服务节点
func ChooseRandomDataServer() string{
    ds := GetDataServers()
    n := len(ds)
    if n == 0 {
        return  ""
    }
    return ds[rand.Intn(n)]
 
 
}

  

接口服务的定义处理locate

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
package Locate
 
import (
    rabbitMQ "RabbitMQ"
    "encoding/json"
    "fmt"
    "net/http"
    "strconv"
    "strings"
    "time"
)
//接口服务的定位服务处理
func Handler(w http.ResponseWriter, r  *http.Request){
    //获取url信息进行分割,并定位
    info := Locate(strings.Split(r.URL.EscapedPath(),"/")[2])
    if len(info) == 0{
        s := "资源不存在"
        w.Write([]byte(s))
 
    }
    b, _:= json.Marshal(info)
    w.Write(b)
}
 
//向dataServers exchge发送定位信息并接收
func Locate(name string)string{
    q := rabbitMQ.New("rabbitmq链接","")
    //先定位
    q.Publish("dataServers",name,"")
    fmt.Printf("定位对象为:%s\n",name)
    //再接收,如果要接收信息,必须要有信道
    c := q.Consume()
    go func(){
        time.Sleep(time.Second)
        q.Close()
    }()
    msg := <-c
    s,_ := strconv.Unquote(string(msg.Body))
    fmt.Printf("目标锁定:%s\n",s)
    return s
}
 
func Exist(name string) bool{
    return Locate(name) != ""
}

  

 

 

 

 

 

 

 

 

  

 

posted @   -零  阅读(494)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
历史上的今天:
2019-03-19 博客开发系列(二)之分类归类与日期归类的实现
2019-03-19 Jquery操作一遍过
点击右上角即可分享
微信分享提示