数据服务的分布式模型

数据服务的分布式模型

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

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

模型

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

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

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

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

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

扫描二维码关注公众号,回复: 9970956 查看本文章

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

  

  

猜你喜欢

转载自www.cnblogs.com/-wenli/p/12526878.html