使用java中的nginx进行端口转发

时间:2013-05-28 10:28:04

标签: java ssl https nginx redis

我正在尝试创建一个 java应用程序,它使用 redis 作为后端。由于 Redis 是我想要使用的非常快速的键值存储,但是 redis 可以与1个客户端一起使用,因此它没有选项对于用户:通过身份验证。我想找到一种方法来实现某种身份验证,所以我尝试使用 redis2 扩展名 nginx 。我这样做是因为我可以使用客户端证书和HTTPS。但它使我的应用程序变得非常慢。

我正在考虑使用某种通过 nginx代理连接到redis的隧道。对于这个redis会在localhost上监听,并且会有一个我想用来达到redis的地址,但是使用https身份验证。基本上我的当前方法

JAVA - Jedis - LAN - REDIS ,would be 
JAVA - Jedis(with localhost as the tunnel entrance?)-
-SSL LAN - Nginx(tunnel exit) - Redis

实现这一目标的任何提示?我已经在网上搜索了最后几天,但我无法提出任何只增加本机连接的开销。

2 个答案:

答案 0 :(得分:3)

Redis旨在在后端应用程序后面的安全网络上工作。客户端应用程序不应直接连接到Redis。这使得Redis成为2层应用程序的不良选择。

现在,如果你仍想使用Redis,你有几个选择。您可以将Redis服务器封装在HTTP接口中。这就是nginx redis2模块提供的功能。您可能还想查看webdis,它类似(并且不依赖于nginx)。 Webdis提供了一些访问控制机制。请参阅文档。

另一种解决方案是按照您的建议建立隧道。我不会使用nginx,但只是普通的旧SSH。假设Redis服务器在机器B(端口6379)上运行,客户端在机器A上运行。

在机器A上,我可以运行:

ssh user@host_B -L 7008:host_B:6379 -N

它将从本地端口7008(任意选择)打开从A到B的隧道,然后等待。用户应在主机B上声明,并且密码已知。在另一个会话中,仍然在主持人A上,我们现在可以运行:

redis-cli -p 7008 ping

请注意使用标准的Redis客户端。隧道以透明的方式为客户端处理身份验证,加密和可选的压缩。

现在,您的客户端是Java应用程序,您可能不希望运行SSH命令来设置隧道。希望您可以使用Jsch包直接从Java打开隧道。以下是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();
    }
 }

它工作正常,但请注意由于隧道有一个开销。当网络速度较慢时(例如Internet),开销非常低。在快速LAN(1 GbE)上,它更加引人注目:当使用隧道时,延迟可以乘以最多3。 Redis服务器可以承受的最大吞吐量也会受到影响。在服务器端,sshd守护进程需要一些CPU(超过Redis本身)。

尽管如此,我并不认为原始性能对于双层应用程序很重要。

答案 1 :(得分:2)

注意:有一个名为SSL-REDIS的SSL版本可以在github上找到: https://github.com/bbroerman30/ssl-redis 2.6ish

https://github.com/tritondigital/ssl-redis 2.4ish

通过这个并修改Jedis Java客户端,可以实现SSL身份验证