我们上次讲到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;

}

}

第二步,代理服务接入:

@Override

protected 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事件,逻辑处理完毕。

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

@Override

public 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都启动感兴趣消息读事件(写事件是在读取处理中自动触发),为什么在这里做的原因是一定要等上下游都正常态后才启动双方消息处理,不然会出现字节丢失。

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

@Override

protected void onMessage(Object msg, Session sess) throws IOException {

Session chain = sess.chain;

if(chain == null){

sess.asyncClose();

return;

}

chain.write(msg);

}

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

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

@Override

public 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示例代码库中找到

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;

}

}

@Override

protected 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);

}

@Override

public 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);

}

}

@Override

protected void onMessage(Object msg, Session sess) throws IOException {

Session chain = sess.chain;

if(chain == null){

sess.asyncClose();

return;

}

chain.write(msg);

}

@Override

public 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();

}

}

mysql 透明代理_透明代理MySQL_基于zbus的MySQL透明代理(100行)-云栖社区相关推荐

  1. mysql计划任务书_求做一个基于java的mysql课程设计任务书 要求如下

    课程设计任务书一.课程设计的目标使学生综合使用所学过的Java语言程序设计知识,掌握面向对象程序设计的基本思路和方法,利用所学的基本知识和技能,发挥自学能力和查找资料的能力,... 课程设计任务书 一 ...

  2. 基于zbus的MySQL透明代理(100行)

    项目地址 https://git.oschina.net/rushmore/zbus 我们上次讲到zbus网络通讯的核心API: Dispatcher -- 负责-NIO网络事件Selector引擎的 ...

  3. docker 打包mysql_基于docker部署mysql的数据持久化问题

    本人最近在使用docker部署mysql时,在持久化mysql数据时遇到了一个有趣的问题,将排查过程及思考记录在这里,以备后查. 先简单描述下我遇到的问题:在mysql容器中创建了两个数据库,然后使用 ...

  4. postgresql 远程用户_构建Python pandas基于SSH远程MySQL和PostgreSQL的数据分析

    背景知识视频教程 Python中使用Pandas教程 - 国外课栈​viadean.com Pandas数据分析与探索 - 国外课栈​viadean.com 如果您无法从外部环境直接访问数据库,则可能 ...

  5. 2台mysql集群_如何安装配置基于2台服务器的MySQL集群

    这篇文章旨在介绍如何安装配置基于2台服务器的MySQL集群.并且实现任意一台服务器出现问题或宕机时MySQL依然能够继续运行. 注意!虽然这是基于2台服务器的MySQL集群,但也必须有额外的第三台服务 ...

  6. 成绩查询系统 mysql_基于PHP和MYSQL的成绩查询系统

    作者简介:刘杰(1980-),男,湖北黄冈人,中国地质大学(武汉)信息工程学院硕士研究生,研究方向为计算机应用. 基于 PHP 和 MYSQL 的成绩查询系统 刘 杰 (中国地质大学 信息工程学院,湖 ...

  7. PHP与MySQL外文文献译文和原文_计算机外文翻译---基于PHP和MYSQL的网站设计和实现...

    计算机外文翻译---基于PHP和MYSQL的网站设计和实现 中文 2270 字 译文二 基于 PHP 和 MYSQL 的网站设计和实现 摘要 PHP 和 MYSQL 因为其免费以及开放源码已经成为主要 ...

  8. mysql序列号生成_值得一看!数据库及Mysql入门,附详细安装教程

    #什么是数据 用来描述事物的符号记录.可以是数字.文字.图形等,有多种形式,经过数字化之后存入计算机 #什么是数据库 数据库(Database)就是一个用来存放数据库的仓库,是按照一定的数据结构来组织 ...

  9. mysql5.7延迟_[MySQL] 号称永久解决了复制延迟问题的并行复制,MySQL5.7-阿里云开发者社区...

    原文:[MySQL] 号称永久解决了复制延迟问题的并行复制,MySQL5.7 一.缘由: 某天看到主从复制延时的告警有点频繁,就想着是不是彻底可以解决一下. 一般主从复制,有三个线程参与,都是单线程: ...

最新文章

  1. flannel原理初探针对0.1.0版本
  2. 凡人学习Linux之路
  3. java 反射field_Java基础--反射之Field
  4. leetcode刷题集:栈与队列
  5. angular4获得焦点事件_Angular 4 文本框自动获取焦点二
  6. c语言枚举常量,浅述C语言中枚举enum的用法
  7. java对象复制 忽略空值_优秀!高级Java都这样优雅处理空值
  8. 天然气阶梯是按年还是按月_您搞懂了吗?阶梯电费是按年计算而不是按月计算的...
  9. 微星主板更新BIOS能识别U盘但读取不到BIOS文件
  10. 医院信息化整体解决方案
  11. Jshack网络验证系统,又名 JS下锅云网络验证系统-免费网络验证系统
  12. error code
  13. 3小时入门微信小程序开发 --公开课学习笔记
  14. python绘制箭头_python如何绘制坐标箭头?
  15. cosface:large margin cosine loss for deep face recognition
  16. P2178 [NOI2015] 品酒大会(并查集+后缀数组)
  17. CHI的Cache Stashing和DVM操作
  18. Vuex前端saas人力资源中台管理项目第五天 权限管理和图表设计
  19. php 模拟数猴子数大王,猴子大王的问题
  20. 如何把通达信公式变成python_通达信公式-主力雷达Python化

热门文章

  1. ckeditor4 php,CKEditor 4自定义下拉列表
  2. python语言 行业_如何入门编程开发行业 选择Python语言怎么样
  3. bp神经网络预测模型_基于BP神经网络模型的河南省严重精神障碍患者服药依从性影响因素分析...
  4. larvel 中的api.php_Laravel API 系列教程(一): 基于 Laravel 5.5 构建 测试 RESTful API...
  5. 黑龙江认识电子计算机ppt,《第22课 不断发展的现代社会》优秀教案(黑龙江县级优课).docx...
  6. python查询数据库带逗号_浅谈pymysql查询语句中带有in时传递参数的问题
  7. Python:粘包问题
  8. 没看完这11 条,别说你精通 Python 装饰器
  9. 怎么html中加样式,简明教程 在HTML中添加样式表的方法
  10. linux C语言如何获取进程号和线程号?getpid()、syscall(__NR_gettid)