项目地址 https://git.oschina.net/rushmore/zbus

我们上次讲到zbus网络通讯的核心API:

Dispatcher -- 负责-NIO网络事件Selector引擎的管理,对Selector引擎负载均衡

IoAdaptor -- 网络事件的处理,服务器与客户端共用,负责读写,消息分包组包等

Session -- 代表网络链接,可以读写消息

实际的应用,我们几乎只需要做IoAdaptor的个性化实现就能完成高效的网络通讯服务,今天我们将举例说明如何个性化这个IoAdaptor。

我们今天要完成的目标是:实现MySQL服务器的透明代理。效果是,你访问代理服务器跟访问目标MySQL无差异。

我们在测试环境10.17.2.30:3306 这台机器上提供了MySql,在我们本地机器上跑起来我们今天基于zbus.NET实现的一个代理程序,就能达到下面的效果。


完成大概不到100 行的代码, Cool?Let’s roll!

首先,我们思考透明TCP代理到底在干啥,透明的TCP代理的业务逻辑其实非常简单,可以描述为,将来自代理上游(发起请求到代理)的数据转发到目标TCP服务器,把目标服务器回来的数据原路返回代理上游客户端。 注意这个原路,如何做到原路返回成为关键点。这个示例其实跟MySQL没有任何关系,原则上任何TCP层面的服务都应该适配。

基于zbus.NET怎么来将上面的逻辑在体现出来,也就是如何个性化IoAdaptor?直观的讲,我们要处理的几个事件应该包括:1)从上游客户端发起的链接请求--代理服务器的Accept事件,2)代理服务器连接目标服务器的Connect事件,3)上下游的数据事件onMessage。

zbus.NET的IoAdaptor提供的个性化事件如下

基本包括一个链接(客户端或者服务端)的生命周期,与消息的编解码。

我们的代理IoAdaptor就是逐一个性化处理。

第一步,编解码: 透明代理对消息内容不做理解,所以不需要编解码。

// 透传不需要编解码,简单返回ByteBuffer数据public IoBuffer encode(Object msg) {if (msg instanceof IoBuffer) {IoBuffer buff = (IoBuffer) msg;return buff;} else {throw new RuntimeException("Message Not Support");}}// 透传不需要编解码,简单返回ByteBuffer数据public Object decode(IoBuffer buff) {if (buff.remaining() > 0) {byte[] data = new byte[buff.remaining()];buff.readBytes(data);return IoBuffer.wrap(data);} else {return null;}}

第二步,代理服务接入:

@Overrideprotected void onSessionAccepted(Session sess) throws IOException {Session target = null;Dispatcher dispatcher = sess.getDispatcher();try {target = dispatcher.createClientSession(targetAddress, this);} catch (Exception e) {sess.asyncClose();return;}sess.chain = target;target.chain = sess;dispatcher.registerSession(SelectionKey.OP_CONNECT, target);}

这里的逻辑思路是,代理服务器每接受到一个请求--通过onSessionAccepted表达,我们将同时创建一个到目标服务器的链接,今天的例子是目标MySQL服务器,注意上面的处理中把创建目标服务器Session过程与真正链接到目标服务分开(Dispatcher也提供合并二者的工具方法),是为了能在没有发生链接之前绑定上好上下游关系,通过Session的chain变量来表达,也就是当前Session的关联Session,关联好之后启动感兴趣Connect事件,逻辑处理完毕。

第三步,链接成功事件(第二步中需要链接到目标服务器)

@Overridepublic void onSessionConnected(Session sess) throws IOException {  Session chain = sess.chain;if(chain == null){ sess.asyncClose();return; }   if(sess.isActive() && chain.isActive()){ sess.register(SelectionKey.OP_READ);chain.register(SelectionKey.OP_READ);}}

这里的一个核心是当上下游都处于链接正常态,上下游Session都启动感兴趣消息读事件(写事件是在读取处理中自动触发),为什么在这里做的原因是一定要等上下游都正常态后才启动双方消息处理,不然会出现字节丢失。

第四步,处理上下游数据事件

@Overrideprotected void onMessage(Object msg, Session sess) throws IOException {  Session chain = sess.chain;if(chain == null){sess.asyncClose(); return;} chain.write(msg); }

是不是非常简单,类似pipeline,从一端的数据写到另外一端。

原则上面4步结束,整个透明代理就完成了,但是为了处理链接异常清理,我们增加了Session清理处理,如下

@Overridepublic void onSessionToDestroy(Session sess) throws IOException {   try {sess.close();} catch (IOException e) { //ignore} if (sess.chain == null) return; try {    sess.chain.close();    sess.chain.chain = null;sess.chain = null;} catch (IOException e) { }}

工作就是解决上下游链接清理链接。

至此为止我们的IoAdaptor个性化就完成了,是不是非常简单,现在我们要跑起来测试了,下面的代码就是上一次讲到重复的设置,没有新意。

public static void main(String[] args) throws Exception {   Dispatcher dispatcher = new Dispatcher(); IoAdaptor ioAdaptor = new TcpProxyAdaptor("10.17.2.30:3306"); final Server server = new Server(dispatcher, ioAdaptor, 3306); server.start();}

骚年,包括渣渣import和少许注释加起来折腾了不到100行,该跑一跑了,还是那句话,不是HelloWorld,你可以规模压力测。看看你是否在本地代理出来了你的目标服务MySQL,gl,hf, gogogo.

完整代码可运行代码如下,也可直接到zbus示例代码库中找到

https://git.oschina.net/rushmore/zbus/blob/master/src/test/java/org/zbus/net/TcpProxyAdaptor.java?dir=0&filepath=src%2Ftest%2Fjava%2Forg%2Fzbus%2Fnet%2FTcpProxyAdaptor.java&oid=08abff381d93519485e1c0ee2c35f1d4f8d1814c&sha=a29272ed99a8f21ec19a14b403ebee53a385e9a4

package org.zbus.net;
import java.io.IOException;
import java.nio.channels.SelectionKey;
import org.zbus.net.core.Dispatcher;
import org.zbus.net.core.IoAdaptor;
import org.zbus.net.core.IoBuffer;
import org.zbus.net.core.Session;
public class TcpProxyAdaptor extends IoAdaptor {private String targetAddress;public TcpProxyAdaptor(String targetAddress) {this.targetAddress = targetAddress;}// 透传不需要编解码,简单返回ByteBuffer数据public IoBuffer encode(Object msg) {if (msg instanceof IoBuffer) {IoBuffer buff = (IoBuffer) msg;return buff;} else {throw new RuntimeException("Message Not Support");}}// 透传不需要编解码,简单返回ByteBuffer数据public Object decode(IoBuffer buff) {if (buff.remaining() > 0) {byte[] data = new byte[buff.remaining()];buff.readBytes(data);return IoBuffer.wrap(data);} else {return null;}}@Overrideprotected void onSessionAccepted(Session sess) throws IOException {Session target = null;Dispatcher dispatcher = sess.getDispatcher();try {target = dispatcher.createClientSession(targetAddress, this);} catch (Exception e) {sess.asyncClose();return;}sess.chain = target;target.chain = sess;dispatcher.registerSession(SelectionKey.OP_CONNECT, target);}@Overridepublic void onSessionConnected(Session sess) throws IOException {  Session chain = sess.chain;if(chain == null){ sess.asyncClose();return; }   if(sess.isActive() && chain.isActive()){ sess.register(SelectionKey.OP_READ);chain.register(SelectionKey.OP_READ);}}@Overrideprotected void onMessage(Object msg, Session sess) throws IOException {  Session chain = sess.chain;if(chain == null){sess.asyncClose(); return;} chain.write(msg); }@Overridepublic void onSessionToDestroy(Session sess) throws IOException {   try {sess.close();} catch (IOException e) { //ignore} if (sess.chain == null) return; try {    sess.chain.close();    sess.chain.chain = null;sess.chain = null;} catch (IOException e) { }}@SuppressWarnings("resource")public static void main(String[] args) throws Exception {   Dispatcher dispatcher = new Dispatcher(); IoAdaptor ioAdaptor = new TcpProxyAdaptor("10.17.2.30:3306"); final Server server = new Server(dispatcher, ioAdaptor, 3306);server.setServerName("TcpProxyServer");server.start();}
}

文章转载自 开源中国社区[https://www.oschina.net]

基于zbus的MySQL透明代理(100行)相关推荐

  1. mysql 透明代理_透明代理MySQL_基于zbus的MySQL透明代理(100行)-云栖社区

    我们上次讲到zbus网络通讯的核心API: Dispatcher -- 负责-NIO网络事件Selector引擎的管理,对Selector引擎负载均衡 IoAdaptor -- 网络事件的处理,服务器 ...

  2. 透明代理解决方案(一)

    透明代理解决方案(一) 最近做基于 Iptables REDIRECT 的透明代理方案时遇到一个问题,解决的过程中涉及到几个重要的网络相关知识点,记录在这里以便查找. 方案说明 网上有很多种透明代理的 ...

  3. 100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)

    ===================================================== 最简单的基于FFmpeg的视频播放器系列文章列表: 100行代码实现最简单的基于FFMPEG ...

  4. mysql查询前100行和后100行

    查询前100行 SELECT * FROM table LIMIT 100; 查询后100行 select * from table order by id desc limit 100: 查询第6- ...

  5. 100行代码实现最简单的基于FFMPEG+SDL的视频播放器

    简介 FFMPEG工程浩大,可以参考的书籍又不是很多,因此很多刚学习FFMPEG的人常常感觉到无从下手.我刚接触FFMPEG的时候也感觉不知从何学起. 因此我把自己做项目过程中实现的一个非常简单的视频 ...

  6. 基于amoeba实现mysql数据库的读写分离/负载均衡

    更多博文请关注:没有伞的孩子必须努力奔跑 (www.xuchanggang.cn) 一.Amoeba的简述:[来自百度百科]      Amoeba是一个以MySQL为底层数据存储,并对应用提供MyS ...

  7. squid缓存服务器 ACL访问控制 传统代理 透明代理 squid日志分析 反向代理

    缓存代理概述 Squid提供了强大的代理控制机制,通过合理的设置ACL,并进行限制,可以针对源地址.目标地址.访问的URL路径.访问的时间等条件进行过滤. 作为应用层的代理服务器软件,Sqiod主要提 ...

  8. linux中设置透明代理

    1. 什么是透明代理? 如果你问:我如何才能使得用户的浏览器不需要任何代理设置就能使用我的 Squid cache 代理服务器上网?,此时你就需要使用透明代理.透明代理让你的客户端不需设置任何代理,当 ...

  9. 图解正向代理、反向代理、透明代理

    内容出自:http://z00w00.blog.51cto.com/515114/1031287 套用古龙武侠小说套路来说,代理服务技术是一门很古老的技术,是在互联网早期出现就使用的技术.一般实现代理 ...

最新文章

  1. python求五个数中的最大值和最小值_python编程 求输入的10个数中的最大值和最小值,并输出它们各自是第几个...
  2. python 双向链表_数据结构-双向链表(Python实现)
  3. 快速入门系列之 Scala 语言 GitChat连接
  4. linux下 如何用信号signal 终止或者暂停子线程,linux下实现线程暂停
  5. 不用归一化,深度学习模型也可以很优秀!
  6. Bootstrap表单控件的尺寸
  7. POJ 2488 A Knight's Journey
  8. HTML判断如果并且,将html转换为js,并且其中含有判断语句
  9. jmeter使用if控制器_Jmeter 常见逻辑控制器详解
  10. java经常用到的英文_Java中用到的英文单词,你知道多少?
  11. 基于Pygame框架和蒙特卡洛树搜索的“走四棋儿”人机对战小游戏(附编程详解和代码)
  12. mysql开启url重写_开启URL伪静态的方法
  13. linux汉诺塔实验报告,汉诺塔问题实验报告
  14. 课时5 企业Web服务器现场抓鸡案例分享
  15. 怎么防治计算机病毒,计算机病毒怎么防治
  16. antd组件:Table表格去掉表格边框线,#字类型表格
  17. SEM和TEM的相同点和不同点
  18. 创建一个网站需要多少预算?
  19. VS2019安装教程(C语言)
  20. vs2015 产品密钥

热门文章

  1. VMware创建Ubuntu操作系统到网络配置详细流程
  2. 鸿蒙科技与文化,数字阅读 | “华为鸿蒙”:当现代科技遇到古典文化
  3. mysql慢查询开启语句分析_mysql慢查询语句分析总结
  4. Qt中的QByteArray和自定义结构体之间的相互转换
  5. 二叉树的操作(前,中,后序遍历也叫深度优先遍历,非空结点的个数)递归实现
  6. CentOS 7 安装nginx
  7. 83. 删除排序链表中的重复元素
  8. IO多路复用之epoll
  9. Redis有几种数据类型?文末领取面试资料
  10. springboot 返回json字符串格式化问题