Haproxy反向代理MySQL底层原理分析
最近研究了一下Haproxy反向代理的机制,它支持俩中模式的反向代理,有4层模式与7层模式。
@:使用7层模式作为Web代理,下面是官方的解释,按照http模式工作,客户端的请求在被转发到真实服务器之前会被代理服务器深度分析,任何请求如果不满足(RFC-compliant)Web标准将被拒绝,会对请求进行过滤 处理 和 路由。也就是与目前比较流行的Nginx原理都差不多,这里不做过多的解释。
http The instance will work in HTTP mode. The client request will be
analyzed in depth before connecting to any server. Any request
which is not RFC-compliant will be rejected. Layer 7 filtering,
processing and switching will be possible. This is the mode which
brings HAProxy most of its value.
@:使用4层模式作为TCP/IP协议的代理,下面是官方的解释,按照tcp的模式工作,全双工的tcp连接建立在客户端与服务器之间,没有7层模式的检查,默认模式。可以支持 SSL SSH SMTP等等..
tcp The instance will work in pure TCP mode. A full-duplex connection
will be established between clients and servers, and no layer 7
examination will be performed. This is the default mode. It
should be used for SSL, SSH, SMTP, ...
@:看了文档之后发现haproxy所谓的4层代理,我个人认为它并不是真正意义上的4层代理,:haproxy的4层tcp模式的工作原理如下图。
@:haproxy的4层tcp模式的工作原理是这样的。 1:客户端与haproxy服务器建立TCP连接,Haproxy在收到客户端的TCP连接后,会与真实服务器建立一个TCP连接,并与之对应起来。 2:客户端发送数据到Haproxy服务器,系统内核会把数据包交给应用层(Haproxy)。备注:这里数据已经到应用层了,所以我说它是伪4层。 3:Haproxy软件会把数据流原封不动的发送之前与真实服务器建立的TCP连接。这样就发成了对TCP数据流的转发。 4:真实服务器返回数据的过程与上面一个道理。
@:根据上面的理论,我用Java简单写个程序还验证一下。 其中:192.168.80.111", 3306 是数据库的IP与端口,本机192.168.1.125充当代理服务器监听3306端口,在用一台机器充当客户端。
package com.framework.code.demo;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class MySqlReplicationDriverDemo {
public static void main(String[] args) throws Exception {
//监听本机3306端口,本机作为代理服务器
ServerSocket serverSocket = new ServerSocket(3306);
//这里为了测试只能与一个客户端进行连接
final Socket socket = serverSocket.accept();
//对应客户端的输出流
final InputStream clientInputStream = socket.getInputStream();
//对应客户端的输入流
final OutputStream clientOutputStream = socket.getOutputStream();
//与真实后端服务器进行tcp连接
Socket mysqlSocker = new Socket("192.168.80.111", 3306);
//对应后端输入流
final InputStream realInputStream = mysqlSocker.getInputStream();
//对应后端输出流
final OutputStream realOutputStream = mysqlSocker.getOutputStream();
//启动一个线程,负责读取客户端的输入数据,从客户端读取的所有数据全部转发给后端真实服务器
//clientInputStream >> realOutputStream
new Thread(new Runnable() {
@Override
public void run() {
try {
byte[] cache = new byte[2048];
while(true) {
//读取客户端的二进制数据
int index = clientInputStream.read(cache);
if(index == -1) {
return;
}
//全部发送给真实服务器
realOutputStream.write(cache, 0, index);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
//启动一个线程,负责读取真实服务器的输入数据,全部发送给客户端
//realInputStream >> clientOutputStream
new Thread(new Runnable() {
@Override
public void run() {
try {
byte[] cache = new byte[2048];
while(true) {
//读取真实服务器的二进制数据
int index = realInputStream.read(cache);
if(index == -1) {
return;
}
//全部发送给客户端
clientOutputStream.write(cache, 0, index);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
Thread.sleep(300000);
}
}