Redis
 sql >> Teknologi Basis Data >  >> NoSQL >> Redis

Ketersediaan Tinggi dengan Redis Sentinel:Menghubungkan ke Redis Master/Slave Set

Menghubungkan ke server Redis tunggal yang berdiri sendiri cukup sederhana:cukup arahkan ke host, port, dan berikan kata sandi otentikasi, jika ada. Sebagian besar klien Redis bahkan menyediakan dukungan untuk beberapa jenis spesifikasi koneksi URI juga.

Namun, untuk mencapai Ketersediaan Tinggi (HA), Anda perlu menerapkan konfigurasi master dan slave. Dalam postingan ini, kami akan menunjukkan cara menyambungkan ke server Redis dalam konfigurasi HA melalui satu titik akhir.

Ketersediaan Tinggi di Redis

Ketersediaan tinggi di Redis dicapai melalui replikasi master-slave. Server master Redis dapat memiliki beberapa server Redis sebagai budak, lebih disukai ditempatkan pada node yang berbeda di beberapa pusat data. Saat master tidak tersedia, salah satu budak dapat dipromosikan menjadi master baru dan terus melayani data dengan sedikit atau tanpa gangguan.

Mengingat kesederhanaan Redis, ada banyak alat ketersediaan tinggi yang tersedia yang dapat memantau dan mengelola konfigurasi replika master-slave. Namun, solusi HA paling umum yang disertakan dengan Redis adalah Redis Sentinels. Redis Sentinels berjalan sebagai serangkaian proses terpisah yang dalam kombinasi memantau set master-slave Redis dan menyediakan failover dan konfigurasi ulang otomatis.

Menghubungkan melalui Redis Sentinels

Redis Sentinels juga bertindak sebagai penyedia konfigurasi untuk set master-slave. Artinya, klien Redis dapat terhubung ke Redis Sentinels untuk mengetahui master saat ini dan kesehatan umum dari kumpulan replika master/slave. Dokumentasi Redis memberikan detail tentang bagaimana klien harus berinteraksi dengan Sentinel. Namun, mekanisme koneksi ke Redis ini memiliki beberapa kelemahan:

  • Membutuhkan dukungan klien :Koneksi ke Redis Sentinels memerlukan klien “sadar” Sentinel. Klien Redis paling populer kini telah mulai mendukung Redis Sentinels tetapi beberapa masih belum. Misalnya, node_redis (Node.js), phpredis (PHP), dan scala-redis (Scala) adalah beberapa klien yang direkomendasikan yang masih belum memiliki dukungan Redis Sentinel.
  • Kompleksitas :Mengonfigurasi dan Menghubungkan ke Redis Sentinels tidak selalu mudah, terutama jika penerapannya melintasi pusat data atau zona ketersediaan. Misalnya, Sentinel mengingat alamat IP (bukan nama DNS ) dari semua server data dan penjaga yang pernah mereka temui dan dapat mengalami kesalahan konfigurasi saat node dipindahkan secara dinamis di dalam pusat data. Redis Sentinel juga berbagi informasi IP dengan Sentinel lainnya. Sayangnya, mereka menyebarkan IP lokal yang dapat menjadi masalah jika klien berada di pusat data yang terpisah. Masalah ini dapat menambah kompleksitas yang signifikan baik untuk Operasi maupun Pengembangan.
  • Keamanan :Server Redis sendiri menyediakan otentikasi primitif melalui kata sandi server, Sentinel sendiri tidak memiliki fitur seperti itu. Jadi Redis Sentinel yang terbuka ke Internet memperlihatkan seluruh informasi konfigurasi dari semua master yang dikonfigurasi untuk dikelola. Jadi Redis Sentinels harus selalu digunakan di belakang firewall yang dikonfigurasi dengan benar. Mendapatkan konfigurasi firewall yang benar, terutama untuk konfigurasi multi-zona bisa sangat rumit.

Titik Akhir Tunggal

Titik akhir koneksi jaringan tunggal untuk kumpulan master-slave dapat disediakan dalam banyak cara. Itu bisa dilakukan melalui IP virtual atau memetakan kembali nama DNS atau dengan menggunakan server proxy (Misalnya HAProxy) di depan server Redis. Setiap kali kegagalan master saat ini terdeteksi (oleh Sentinel), nama IP atau DNS gagal ke budak yang telah dipromosikan menjadi master baru oleh Redis Sentinel. Perhatikan bahwa ini membutuhkan waktu dan koneksi jaringan ke titik akhir perlu dibangun kembali. Redis Sentinel mengenali master sebagai down hanya setelah master tersebut mati selama jangka waktu tertentu (default 30 detik) dan kemudian memilih untuk mempromosikan slave. Setelah promosi budak, alamat IP/entri DNS/proxy perlu diubah untuk menunjuk ke master baru.

Menghubungkan ke kumpulan Master-Slave

Pertimbangan penting saat menyambungkan ke kumpulan replika master-slave menggunakan titik akhir tunggal adalah bahwa seseorang harus menyediakan percobaan ulang pada kegagalan sambungan untuk mengakomodasi kegagalan sambungan selama failover otomatis set replika.

Kami akan menunjukkan ini dengan contoh di Java, Ruby, dan Node.js. Dalam setiap contoh, kami sebagai alternatif menulis dan membaca dari cluster HA Redis saat failover terjadi di latar belakang. Di dunia nyata, upaya percobaan ulang akan dibatasi pada durasi atau jumlah tertentu .

Menghubungkan dengan Java

Jedis adalah klien Java yang direkomendasikan untuk Redis.

Contoh Titik Akhir Tunggal

public class JedisTestSingleEndpoint {
...
    public static final String HOSTNAME = "SG-cluster0-single-endpoint.example.com";
    public static final String PASSWORD = "foobared";
...
    private void runTest() throws InterruptedException {
        boolean writeNext = true;
        Jedis jedis = null;
        while (true) {
            try {
                jedis = new Jedis(HOSTNAME);
                jedis.auth(PASSWORD);
                Socket socket = jedis.getClient().getSocket();
                printer("Connected to " + socket.getRemoteSocketAddress());
                while (true) {
                    if (writeNext) {
                        printer("Writing...");
                        jedis.set("java-key-999", "java-value-999");
                        writeNext = false;
                    } else {
                        printer("Reading...");
                        jedis.get("java-key-999");
                        writeNext = true;
                    }
                    Thread.sleep(2 * 1000);
                }
            } catch (JedisException e) {
                printer("Connection error of some sort!");
                printer(e.getMessage());
                Thread.sleep(2 * 1000);
            } finally {
                if (jedis != null) {
                    jedis.close();
                }
            }
        }
    }
...
}

Output dari kode pengujian ini selama failover terlihat seperti:

Wed Sep 28 10:57:28 IST 2016: Initializing...
Wed Sep 28 10:57:31 IST 2016: Connected to SG-cluster0-single-endpoint.example.com/54.71.60.125:6379 << Connected to node 1
Wed Sep 28 10:57:31 IST 2016: Writing...
Wed Sep 28 10:57:33 IST 2016: Reading...
..
Wed Sep 28 10:57:50 IST 2016: Reading...
Wed Sep 28 10:57:52 IST 2016: Writing...
Wed Sep 28 10:57:53 IST 2016: Connection error of some sort! << Master went down!
Wed Sep 28 10:57:53 IST 2016: Unexpected end of stream.
Wed Sep 28 10:57:58 IST 2016: Connected to SG-cluster0-single-endpoint.example.com/54.71.60.125:6379
Wed Sep 28 10:57:58 IST 2016: Writing...
Wed Sep 28 10:57:58 IST 2016: Connection error of some sort!
Wed Sep 28 10:57:58 IST 2016: java.net.SocketTimeoutException: Read timed out  << Old master is unreachable
Wed Sep 28 10:58:02 IST 2016: Connected to SG-cluster0-single-endpoint.example.com/54.71.60.125:6379
Wed Sep 28 10:58:02 IST 2016: Writing...
Wed Sep 28 10:58:03 IST 2016: Connection error of some sort!
...
Wed Sep 28 10:59:10 IST 2016: Connected to SG-cluster0-single-endpoint.example.com/54.214.164.243:6379  << New master ready. Connected to node 2
Wed Sep 28 10:59:10 IST 2016: Writing...
Wed Sep 28 10:59:12 IST 2016: Reading...

Ini adalah program pengujian sederhana. Dalam kehidupan nyata, jumlah percobaan ulang akan ditentukan berdasarkan durasi atau jumlah.

Contoh Redis Sentinel

Jedis juga mendukung Redis Sentinels. Jadi di sini adalah kode yang melakukan hal yang sama seperti contoh di atas tetapi dengan menghubungkan ke Sentinel.

public class JedisTestSentinelEndpoint {
    private static final String MASTER_NAME = "mymaster";
    public static final String PASSWORD = "foobared";
    private static final Set sentinels;
    static {
        sentinels = new HashSet();
        sentinels.add("mymaster-0.servers.example.com:26379");
        sentinels.add("mymaster-1.servers.example.com:26379");
        sentinels.add("mymaster-2.servers.example.com:26379");
    }

    public JedisTestSentinelEndpoint() {
    }

    private void runTest() throws InterruptedException {
        boolean writeNext = true;
        JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels);
        Jedis jedis = null;
        while (true) {
            try {
                printer("Fetching connection from pool");
                jedis = pool.getResource();
                printer("Authenticating...");
                jedis.auth(PASSWORD);
                printer("auth complete...");
                Socket socket = jedis.getClient().getSocket();
                printer("Connected to " + socket.getRemoteSocketAddress());
                while (true) {
                    if (writeNext) {
                        printer("Writing...");
                        jedis.set("java-key-999", "java-value-999");
                        writeNext = false;
                    } else {
                        printer("Reading...");
                        jedis.get("java-key-999");
                        writeNext = true;
                    }
                    Thread.sleep(2 * 1000);
                }
            } catch (JedisException e) {
                printer("Connection error of some sort!");
                printer(e.getMessage());
                Thread.sleep(2 * 1000);
            } finally {
                if (jedis != null) {
                    jedis.close();
                }
            }
        }
    }
...
}

Mari kita lihat perilaku program di atas selama failover terkelola Sentinel:

Wed Sep 28 14:43:42 IST 2016: Initializing...
Sep 28, 2016 2:43:42 PM redis.clients.jedis.JedisSentinelPool initSentinels
INFO: Trying to find master from available Sentinels...
Sep 28, 2016 2:43:42 PM redis.clients.jedis.JedisSentinelPool initSentinels
INFO: Redis master running at 54.71.60.125:6379, starting Sentinel listeners...
Sep 28, 2016 2:43:43 PM redis.clients.jedis.JedisSentinelPool initPool
INFO: Created JedisPool to master at 54.71.60.125:6379
Wed Sep 28 14:43:43 IST 2016: Fetching connection from pool
Wed Sep 28 14:43:43 IST 2016: Authenticating...
Wed Sep 28 14:43:43 IST 2016: auth complete...
Wed Sep 28 14:43:43 IST 2016: Connected to /54.71.60.125:6379
Wed Sep 28 14:43:43 IST 2016: Writing...
Wed Sep 28 14:43:45 IST 2016: Reading...
Wed Sep 28 14:43:48 IST 2016: Writing...
Wed Sep 28 14:43:50 IST 2016: Reading...
Sep 28, 2016 2:43:51 PM redis.clients.jedis.JedisSentinelPool initPool
INFO: Created JedisPool to master at 54.214.164.243:6379
Wed Sep 28 14:43:52 IST 2016: Writing...
Wed Sep 28 14:43:55 IST 2016: Reading...
Wed Sep 28 14:43:57 IST 2016: Writing...
Wed Sep 28 14:43:59 IST 2016: Reading...
Wed Sep 28 14:44:02 IST 2016: Writing...
Wed Sep 28 14:44:02 IST 2016: Connection error of some sort!
Wed Sep 28 14:44:02 IST 2016: Unexpected end of stream.
Wed Sep 28 14:44:04 IST 2016: Fetching connection from pool
Wed Sep 28 14:44:04 IST 2016: Authenticating...
Wed Sep 28 14:44:04 IST 2016: auth complete...
Wed Sep 28 14:44:04 IST 2016: Connected to /54.214.164.243:6379
Wed Sep 28 14:44:04 IST 2016: Writing...
Wed Sep 28 14:44:07 IST 2016: Reading...
...

Seperti yang terlihat dari log, klien yang mendukung Sentinel dapat pulih dari peristiwa failover dengan cukup cepat.

Menghubungkan dengan Ruby

Redis-rb adalah klien Ruby yang direkomendasikan untuk Redis.

Contoh Titik Akhir Tunggal

require 'redis'

HOST = "SG-cluster0-single-endpoint.example.com"
AUTH = "foobared"
...

def connect_and_write
  while true do
    begin
      logmsg "Attempting to establish connection"
      redis = Redis.new(:host => HOST, :password => AUTH)
      redis.ping
      sock = redis.client.connection.instance_variable_get(:@sock)
      logmsg "Connected to #{sock.remote_address.ip_address}, DNS: #{sock.remote_address.getnameinfo}"
      while true do
        if $writeNext
          logmsg "Writing..."
          redis.set("ruby-key-1000", "ruby-value-1000")
          $writeNext = false
        else
          logmsg "Reading..."
          redis.get("ruby-key-1000")
          $writeNext = true
        end
        sleep(2)
      end
    rescue Redis::BaseError => e
      logmsg "Connection error of some sort!"
      logmsg e.message
      sleep(2)
    end
  end
end

...
logmsg "Initiaing..."
connect_and_write

Berikut contoh keluaran selama failover:

"2016-09-28 11:36:42 +0530: Initiaing..."
"2016-09-28 11:36:42 +0530: Attempting to establish connection"
"2016-09-28 11:36:44 +0530: Connected to 54.71.60.125, DNS: [\"ec2-54-71-60-125.us-west-2.compute.amazonaws.com\", \"6379\"] " << Connected to node 1
"2016-09-28 11:36:44 +0530: Writing..."
"2016-09-28 11:36:47 +0530: Reading..."
...
"2016-09-28 11:37:08 +0530: Writing..."
"2016-09-28 11:37:09 +0530: Connection error of some sort!"  << Master went down!
...
"2016-09-28 11:38:13 +0530: Attempting to establish connection"
"2016-09-28 11:38:15 +0530: Connected to 54.214.164.243, DNS: [\"ec2-54-214-164-243.us-west-2.compute.amazonaws.com\", \"6379\"] " << Connected to node 2
"2016-09-28 11:38:15 +0530: Writing..."
"2016-09-28 11:38:17 +0530: Reading..."

Sekali lagi, kode sebenarnya harus berisi sejumlah percobaan ulang.

Contoh Redis Sentinel

Redis-rb juga mendukung Sentinel.

AUTH = 'foobared'

SENTINELS = [
  {:host => "mymaster0.servers.example.com", :port => 26379},
  {:host => "mymaster0.servers.example.com", :port => 26379},
  {:host => "mymaster0.servers.example.com", :port => 26379}
]
MASTER_NAME = "mymaster0"

$writeNext = true
def connect_and_write
  while true do
    begin
      logmsg "Attempting to establish connection"
      redis = Redis.new(:url=> "redis://#{MASTER_NAME}", :sentinels => SENTINELS, :password => AUTH)
      redis.ping
      sock = redis.client.connection.instance_variable_get(:@sock)
      logmsg "Connected to #{sock.remote_address.ip_address}, DNS: #{sock.remote_address.getnameinfo} "
      while true do
        if $writeNext
          logmsg "Writing..."
          redis.set("ruby-key-1000", "ruby-val-1000")
          $writeNext = false
        else
          logmsg "Reading..."
          redis.get("ruby-key-1000")
          $writeNext = true
        end
        sleep(2)
      end
    rescue Redis::BaseError => e
      logmsg "Connection error of some sort!"
      logmsg e.message
      sleep(2)
    end
  end
end

Redis-rb mengelola failover Sentinel tanpa gangguan apa pun.

"2016-09-28 15:10:56 +0530: Initiaing..."
"2016-09-28 15:10:56 +0530: Attempting to establish connection"
"2016-09-28 15:10:58 +0530: Connected to 54.214.164.243, DNS: [\"ec2-54-214-164-243.us-west-2.compute.amazonaws.com\", \"6379\"] "
"2016-09-28 15:10:58 +0530: Writing..."
"2016-09-28 15:11:00 +0530: Reading..."
"2016-09-28 15:11:03 +0530: Writing..."
"2016-09-28 15:11:05 +0530: Reading..."
"2016-09-28 15:11:07 +0530: Writing..."
...
<<failover>>
...
"2016-09-28 15:11:10 +0530: Reading..."
"2016-09-28 15:11:12 +0530: Writing..."
"2016-09-28 15:11:14 +0530: Reading..."
"2016-09-28 15:11:17 +0530: Writing..."
...
# No disconnections noticed at all by the application

Terhubung dengan Node.js

Node_redis adalah klien Node.js yang direkomendasikan untuk Redis.

Contoh Titik Akhir Tunggal

...
var redis = require("redis");
var hostname = "SG-cluster0-single-endpoint.example.com";
var auth = "foobared";
var client = null;
...

function readAndWrite() {
  if (!client || !client.connected) {
    client = redis.createClient({
      'port': 6379,
      'host': hostname,
      'password': auth,
      'retry_strategy': function(options) {
        printer("Connection failed with error: " + options.error);
        if (options.total_retry_time > 1000 * 60 * 60) {
          return new Error('Retry time exhausted');
        }
        return new Error('retry strategy: failure');
      }});
    client.on("connect", function () {
      printer("Connected to " + client.address + "/" + client.stream.remoteAddress + ":" + client.stream.remotePort);
    });
    client.on('error', function (err) {
      printer("Error event: " + err);
      client.quit();
    });
  }

  if (writeNext) {
    printer("Writing...");
    client.set("node-key-1001", "node-value-1001", function(err, res) {
      if (err) {
        printer("Error on set: " + err);
        client.quit();
      }
      setTimeout (readAndWrite, 2000)
    });

    writeNext = false;
  } else {
    printer("Reading...");
    client.get("node-key-1001", function(err, res) {
      if (err) {
        client.quit();
        printer("Error on get: " + err);
      }
      setTimeout (readAndWrite, 2000)
    });
    writeNext = true;
  }
}
...
setTimeout(readAndWrite, 2000);
...

Begini tampilan failover:

2016-09-28T13:29:46+05:30: Writing...
2016-09-28T13:29:47+05:30: Connected to SG-meh0-6-master.devservers.mongodirector.com:6379/54.214.164.243:6379 << Connected to node 1
2016-09-28T13:29:50+05:30: Reading...
...
2016-09-28T13:30:02+05:30: Writing...
2016-09-28T13:30:04+05:30: Reading...
2016-09-28T13:30:06+05:30: Connection failed with error: null << Master went down
...
2016-09-28T13:30:50+05:30: Connected to SG-meh0-6-master.devservers.mongodirector.com:6379/54.71.60.125:6379 << Connected to node 2
2016-09-28T13:30:52+05:30: Writing...
2016-09-28T13:30:55+05:30: Reading...

Anda juga dapat bereksperimen dengan opsi 'retry_strategy' selama pembuatan koneksi untuk menyesuaikan logika coba lagi untuk memenuhi kebutuhan Anda. Dokumentasi klien memiliki contohnya.

Contoh Redis Sentinel

Node_redis saat ini tidak mendukung Sentinels, tetapi klien Redis yang populer untuk Node.js, ioredis mendukung Sentinels. Lihat dokumentasinya tentang cara terhubung ke Sentinel dari Node.js.

Siap untuk meningkatkan? Kami menawarkan hosting untuk Redis™* dan solusi terkelola sepenuhnya di cloud pilihan Anda. Bandingkan kami dengan yang lain dan lihat mengapa kami menghemat kerumitan dan uang Anda.


  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Bagaimana cara mencari string di redis?

  2. Redis daftar kunci bersarang

  3. File konfigurasi Tuhan untuk memantau proses yang ada?

  4. Pemula aplikasi waktu nyata - Node.JS + Redis atau RabbitMQ -> klien/server bagaimana?

  5. Bagaimana cara menerima acara kedaluwarsa Redis dengan simpul?