В мрачной тьме далекого будущего есть только потеря данных. Создание простой репликации данных для ваших приложений.
Репликация. Что это?
Репликация данных - это процесс синхронизации данных между вашими сервисами (базами данных, приложениями и т. Д.). Это может помочь вам не потерять данные в критических инцидентах и легко масштабировать возрастающую нагрузку на ваши сервисы.
Replication description from the Wiki: Replication in computing involves sharing information so as to ensure consistency between redundant resources, such as software or hardware components, to improve reliability, fault-tolerance, or accessibility.
Например, вы также можете сохранить ограничения сторонних служб API, если только один экземпляр вашего приложения делает запросы и передает их другим экземплярам.
Репликация поможет вам сохранить данные при сбое некоторых приложений. В нужный момент это избавит вас от множества стрессов. Просто поверьте.
В порядке. Предположим, я убедил вас, и вы хотите сделать собственную репликацию в своем приложении.
Но как?
Первые шаги. Создание модели транзакции.
Базовая транзакция должна иметь некоторые данные и идентификатор для перевода.
// In my opinion, protobufs is the best solution for the replication. // Our identifier will be unix nano timestamp syntax = "proto3"; message Transaction { int64 timestamp = 1; map<string, bytes> data = 2; }
После его компиляции с помощью protoc в коде будет модель транзакции.
Давайте создадим пул транзакций в качестве наилучшей практики.
var transactionPool = sync.Pool{ New: func() interface{} { return new(Transaction) }, } func GetTransaction() *Transaction { return transactionPool.Get().(*Transaction) } func PutTransaction(transaction *Transaction) { transaction.Reset() transactionPool.Put(transaction) }
Теперь мы можем создавать транзакции:
func NewTransaction(data map[string][]byte) *Transaction { transaction := GetTransaction() transaction.Timestamp = time.Now().UnixNano() transaction.Data = data return transaction }
Делаем репликацию.
Простая репликация должна иметь передатчик и приемник (для транзакций отправки и получения), список полученных транзакций и список любых хостов приложений для отправки транзакций.
type Replication struct { Transmitter chan *Transaction Receiver chan *Transaction Received map[string]int64 Replicas map[string]string }
Создадим логику для транзакций передачи:
func (r *Replication) Transmit() { for { transaction := <-r.Transmitter go func() { body, err := proto.Marshal(transaction) if err == nil { for addr, host := range r.Replicas { //That's url what you services will listen for transactions _, err := http.Post(host + "/replication/receiver", "application/protobuf", bytes.NewReader(body)) if err != nil { log.Println("Master", addr, "is down:", err) } } } else { log.Println("Replication error:", err) } PutTransaction(transaction) }() } }
А для приема транзакций:
func (r *Replication) Receive() { for { transaction := <-r.Receiver go func() { for key, data := range transaction.Data { if timestamp, ok := r.Received[key]; ok && timestamp <= transaction.Timestamp { continue } // Put transaction data to you data store err := database.Client.Put(key, data) if err == nil { r.Received[key] = transaction.Timestamp } else { log.Println("Replication error:", err) } } PutTransaction(transaction) }() } }
Механизм отправки сообщения:
func (r *Replication) SendMessage(data map[string][]byte) { if len(r.Replicas) > 0 { r.Transmitter <- NewTransaction(data) } }
Теперь мы можем запустить репликацию в приложении:
replication := &Replication{ Transmitter: make(chan *Transaction, 2048), Receiver: make(chan *Transaction, 2048), Received: make(map[string]int64), Replicas: hosts, }
Использование репликации
Просто запустите приемник и передатчик в новом потоке
go replication.Receive() go replication.Transmit()
И создайте простой метод получения новых транзакций для других экземпляров:
http.HandleFunc("/replication/receiver", func(writer http.ResponseWriter, request *http.Request) { transaction := GetTransaction() body, _ := ioutil.ReadAll(request.Body) err := proto.Unmarshal(body, transaction) if err == nil { replication.Receiver <- transaction fmt.Fprintln(writer, "Received transaction") } else { fmt.Fprintln(writer, err) } })
Теперь вы можете синхронизировать данные между экземплярами вашего приложения с помощью метода:
replication.SendMessage()
Теперь репликация готова к использованию, осталось поднять несколько экземпляров и указать их хосты в реплике!
Вывод
Репликация действительно может спасти жизнь вашему продукту. Но это не панацея.
Потому что все ваши серверы всегда могут выйти из строя одновременно.
Для полной безопасности наймите хороших DevOpses и арендуйте столько серверов, сколько они вам скажут. Это лучшая идея, которую вы можете воплотить в жизнь.
Репозиторий GitHub с реализацией репликации находится здесь.
📝 Прочтите этот рассказ позже в Журнале.
🗞 Просыпайтесь каждое воскресенье утром и слышите самые интересные истории, мнения и новости недели, ожидающие в вашем почтовом ящике: Получите заслуживающий внимания информационный бюллетень›