1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/pardon110-ShortUrl

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
store.go 3.4 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
pardon110Homestead Отправлено 11.10.2019 07:43 45f339e
package main
import (
"encoding/gob"
"errors"
"io"
"log"
"net/rpc"
"os"
"sync"
)
const saveQueueLength = 1000
// 主从服务器都实现了相同的接口
type Store interface {
Put(url, key *string) error
Get(url, key *string) error
}
// 从服务器持有缓存副本
type ProxyStore struct {
urls *URLStore
client *rpc.Client
}
// 主服务器数据布局,map读写使用了读写锁
type URLStore struct {
urls map[string]string
mu sync.RWMutex
save chan record
}
type record struct {
Key, URL string
}
func NewURLStore(filename string) *URLStore {
s := &URLStore{
urls: make(map[string]string),
}
// 只有主服器才进行持久化写入
if filename != "" {
s.save = make(chan record, saveQueueLength)
if err := s.load(filename); err != nil {
log.Println("Error loading URLStore:", err)
}
// 开启守护协程
go s.saveLoop(filename)
}
return s
}
// 改造成RPC版本
func (s *URLStore) Get(key, url *string) error {
s.mu.RLock()
defer s.mu.RUnlock()
if u, ok := s.urls[*key]; ok {
*url = u
return nil
}
return errors.New("key not found")
}
func (s *URLStore) Set(key, url *string) error {
s.mu.Lock()
defer s.mu.Unlock()
if _, present := s.urls[*key]; present {
return errors.New("key already exists")
}
s.urls[*key] = *url
return nil
}
func (s *URLStore) count() int {
s.mu.RLock()
defer s.mu.RUnlock()
return len(s.urls)
}
func (s *URLStore) Put(url, key *string) error {
for {
*key = genKey(s.count())
if err := s.Set(key, url); err == nil {
break
}
}
if s.save != nil {
s.save <- record{*key, *url}
}
return nil
}
func (s *URLStore) load(filename string) error {
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()
d := gob.NewDecoder(f)
for err == nil {
var r record
if err = d.Decode(&r); err == nil {
s.Set(&r.Key, &r.URL)
}
}
if err == io.EOF {
return nil
}
log.Println("Error decoding URLStore:", err)
return err
}
func (s *URLStore) saveLoop(filename string) error {
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
log.Fatal("Error opening URLStore: ", err)
}
e := gob.NewEncoder(f)
for {
r := <-s.save
if err := e.Encode(r); err != nil {
log.Println("Error saving to URLStore: ", err)
}
}
}
// slave服务器
func NewProxyStore(addr string) *ProxyStore {
// 让从服务器具有rpc客户端调用的能力,建立与主服务器通信
client, err := rpc.DialHTTP("tcp", addr)
if err != nil {
log.Printf("Error constructing ProxyStore: ", err)
}
// 给空的filename让从服务器不去尝试写入或读取磁盘,只从本地副本读取
return &ProxyStore{urls: NewURLStore(""), client: client}
}
// 检查本地缓存有无查询项,无则从主服务器读取,并更新本地缓存,返回
func (s *ProxyStore) Get(key, url *string) error {
// 从本机slave服务器缓存中读取数据
if err := s.urls.Get(key, url); err == nil {
return nil
}
// 从master服务器rpc通信调用数据(此处client隐式使用了golang智能指针)
if err := s.client.Call("Store.Get", key, url); err != nil {
return err
}
// 更新本地缓存数据
s.urls.Set(key, url)
return nil
}
// 调用主服务器put成功后,更新本地缓存副本
func (s *ProxyStore) Put(url, key *string) error {
// rpc call to master
if err := s.client.Call("Store.PUt", url, key); err != nil {
return err
}
s.urls.Set(key, url)
return nil
}

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/pardon110-ShortUrl.git
git@api.gitlife.ru:oschina-mirror/pardon110-ShortUrl.git
oschina-mirror
pardon110-ShortUrl
pardon110-ShortUrl
master