Redis został zaprojektowany do pracy w bezpiecznej sieci, za aplikacją zaplecza. Aplikacje klienckie nie powinny łączyć się bezpośrednio z Redis. To sprawia, że Redis jest kiepskim wyborem dla aplikacji dwuwarstwowych.
Teraz, jeśli nadal chcesz używać do tego Redis, masz kilka opcji. Serwer Redis można hermetyzować w interfejsie HTTP. To właśnie zapewnia moduł nginx redis2. Możesz również rzucić okiem na webdis, który jest podobny (i nie zależy od nginx). Webdis oferuje pewne mechanizmy kontroli dostępu. Zobacz dokumentację.
Innym rozwiązaniem jest utworzenie tunelu, tak jak zaproponowałeś. Nie używałbym do tego nginx, ale zwykły stary SSH. Załóżmy, że serwer Redis działa na komputerze B (port 6379), a klient na komputerze A.
Na komputerze A mogę uruchomić:
ssh [email protected]_B -L 7008:host_B:6379 -N
Otworzy tunel od A do B z lokalnego portu 7008 (dowolny wybór) i czeka. Użytkownik powinien być zadeklarowany na hoście B, a jego hasło znane. W innej sesji, nadal na hoście A, możemy teraz uruchomić:
redis-cli -p 7008 ping
Należy pamiętać, że używany jest standardowy klient Redis. Tunel obsługuje uwierzytelnianie, szyfrowanie i opcjonalnie kompresję w sposób przejrzysty dla klienta.
Teraz twój klient jest aplikacją Java i prawdopodobnie nie chcesz uruchamiać poleceń SSH w celu skonfigurowania tunelu. Mamy nadzieję, że możesz użyć pakietu Jsch, aby otworzyć tunel bezpośrednio z Javy. Oto przykład z Jedis:
import redis.clients.jedis.*;
import java.util.*;
import com.jcraft.jsch.*;
public class TestTunnel {
Jedis jedis;
Session session;
JSch jsch = new JSch();
int port;
// None of the following should be hardcoded
static String USER = "user"; // SSH user on the redis server host
static String PASSWD = "XXXXXXXX"; // SSH user password
static String HOST = "192.168.1.62"; // Redis server host
static int PORT = 6379; // Redis server port
public TestTunnel() {
try {
// Open the SSH session
session = jsch.getSession( USER, HOST, 22 );
session.setPassword( PASSWD );
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
config.put("Compression", "yes");
config.put("ConnectionAttempts","3");
session.setConfig(config);
session.connect();
// Setup port forwarding from localhost to the Redis server
// Local port is ephemeral (given by the OS)
// Jedis connects to localhost using the local port
port = session.setPortForwardingL( 0, HOST, PORT );
jedis = new Jedis( "127.0.0.1", port );
} catch ( JSchException e ) {
// Proper error handling omitted
System.out.println(e);
}
}
public void disconnect() {
jedis.disconnect();
try {
session.delPortForwardingL( port );
session.disconnect();
} catch ( JSchException e ) {
// Proper error handling omitted
System.out.println(e);
}
}
public void mytest( int n ) {
for ( int k = 0; k < n; k++) {
jedis.set("k" + k, "value"+k);
}
System.out.println("Read: "+jedis.get("k0") );
}
public static void main(String[] args) {
TestTunnel obj = new TestTunnel();
obj.mytest(10);
obj.disconnect();
}
}
Działa dobrze, ale pamiętaj, że tunel jest obciążony. Narzut jest bardzo niski, gdy sieć jest wolna (na przykład Internet). W szybkiej sieci LAN (1 GbE) jest to znacznie bardziej zauważalne:opóźnienie można zwielokrotnić nawet do 3, gdy używany jest tunel. Ma to również wpływ na maksymalną przepustowość, jaką może utrzymać serwer Redis. Po stronie serwera demon sshd zajmuje trochę procesora (więcej niż sam Redis).
To powiedziawszy, nie sądzę, by surowa wydajność miała duże znaczenie dla aplikacji dwuwarstwowych.