数据服务的分布式模型
数据服务的分布式模型
分布式系统要求个节点分布在网络上,并通过消息传递合作来完成一个共同的目标。
分布式系统的三大关键特征:
- 节点之间并发工作
- 没有全局锁
- 某个节点发生错误不影响其它节点
模型
接口服务层提供对外的REST接口,而数据服务提供对接口服务的REST接口。
接口服务接收用户请求,数据服务接收接口服务的请求,接口服务作为HTTP客户端向数据服务请求数据。
1.每一个数据服务节点都需要向所有的接口服务通知自身存在,这里数据服务需要周期性向接口服务发送心跳包ip:port,而接口服务需要处理心跳包,维持可用的数据服务列表。
注意:心跳包,数据服务向apiServers此交换器发送信息,每个接口服务启动自动绑定此交换器,就可以接收到此交换器的信息。
2.接口服务接收到用户请求后,需要定位数据被保存在哪个数据服务节点上。
注意:定位,接口服务向dataServers此交换器发送信息,每个数据服务启动自动绑定此交换器,就可以结束此交换器的信息。
所以这里接口服务与数据服务是使用Rabbitmq消息队列通信。

数据服务data server
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
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
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
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) != ""
}

浙公网安备 33010602011771号