5.8 Provider间数据交换
-
5.8.4 客户端开发
所有类型客户端均支持隧道数据转发。
-
C++/C#/Java 客户端,以Java版本为例
实现limax.endpoint.EndpointListener的同时,并列实现limax.endpoint.TunnelSupport接口。
public interface TunnelSupport { void onTunnel(int providerid, int label, Octets data) throws Exception; void registerTunnelSender(TunnelSender sender); } public interface TunnelSender { void send(int providerid, int label, Octets data) throws InstantiationException, SizePolicyException, CodecException, ClassCastException; }
实现onTunnel,即可收到服务器发送过来的隧道数据。Limax框架决定了,一个Endpoint可以同时连接多个Provider,这里的providerid进行了区分。onTunnel可以实现为即时转发,也可以暂存数据(暂存时长不可超过配置的有效期),以后转发。
Endpoint初始化时,将回调registerTunnelSender,必须实现registerTunnelSender将当前Endpoint的tunnelSender保存下来,之后在该tunnelSender上转发隧道数据。
通常情况下,客户端需要使用两个Endpoint,EA,EB分别连接隧道两边的Provider,EA.onTunnel收到隧道数据时,通过EB.tunnelSender转发。
这里必须当心,TunnelSupport.onTunnel中的providerid是源Provider的id, TunnelSender.send中的providerid是目的Provider的id,不能像label,data一样直接转发。
不支持TunnelSupport的Endpoint,忽略接收到的隧道数据。
-
脚本客户端,以Javascript版本为例
脚本客户端在创建Limax实例时,将一个函数作为第三个参数传入,即可支持隧道数据接收。
var limax = Limax(function(ctx) { ctx.onerror = function(e) { print('limax error', e); } ctx.onclose = function(e) { print('limax close', e); } ctx.onopen = function() { ......................... } }, cache, function(pvid, label, data) { });
limax实例本身就是一个函数,直接执行 limax(pvid, label, data); 即可发送隧道数据。
-
使用了ScriptEngineHandle的混合客户端(Java/javscript,C#/javascript,C#/Lua,C++/javascript,C++/Lua,各种形式的极简模式客户端),以Java/javascript为例
使用带有TunnelReceiver参数的构造函数创建相应的ScriptHandle,即可实现隧道数据的接收。
new JavascriptHandle(new ScriptEngineManager().getEngineByName("javascript"), new InputStreamReader(Main.class.getResourceAsStream("example.js")), null, null, new TunnelReceiver() { @Override public void onTunnel(int providerid, int label, String data) { System.out.println(providerid + " " + label + " " + data); } });
这里的data来自脚本系统所以类型为String,实际上,这是Octets类型隧道数据的Base64编码结果。
注意观察ScriptEngineHandle接口的定义
public interface ScriptEngineHandle { ....................... void tunnel(int providerid, int label, String data) throws Exception; void tunnel(int providerid, int label, Octets data) throws Exception; }
ScriptEngineHandle上直接调用tunnel方法即可发送隧道数据。来自于脚本系统的隧道数据使用带有String参数的方法发送,来自TunnelSupport的隧道数据使用带有Octets参数的方法发送。
注意:
1. 如果客户端的EndpointListener实现了TunnelSupport接口,又使用了支持TunnelReceiver的ScriptEngineHandle,同一份隧道数据,两处都能接收到。
2. 如果客户端的EndpointListener实现TunnelSupport时记录了TunnelSender,又使用了ScriptEngineHandle,两处都可以用来发送隧道数据。
-
-
5.8.5 运行管理
-
配置文件
Provider配置文件中,Provider节点内,需要添加一个Tunnel节点。
<Provider ...> <Tunnel compressor="ZIP" keyProtector="HMACSHA256" defaultExpire="86400000" defaultGroup="prjA"> <PKIX location="pkcs12:/work/keyalloc.p12" passphrase="123456" revocationCheckerOptions="SOFT_FAIL"/> <SharedKey group="prjA" key="123456" /> <SharedKey group="prjB" key="123456" /> <Expire group="prjA" value="3600"/> </Tunnel> <Manager .../> <Manager .../> ... <'/Provider>
Tunnel节点4个属性
compressor:源数据压缩方法,可选值NONE,RFC2118,ZIP,默认为NONE。参见附录《外部数据》
keyProtector:源数据保护算法,可选值HMACSHA224,HMACSHA256,HMACSHA384,HMACSHA512,TripleDESCBC,AESCBC128,默认为HMACSHA256。参见附录《外部数据》
defaultExpire:隧道数据默认过期时间,单位毫秒,默认3600000。过期时间的配置必须小于key寿命,参见附录《Key分发系统》
defaultGroup:默认信任域。
Tunnel节点下支持2种节点,PKIX与SharedKey,PKIX节点具有高优先级,配置了PKIX节点SharedKey节点被忽略。
配置了多个PKIX节点,第一个节点有效,PKIX节点配置了Key分发系统客户端,请求Key分发网络分配Key保护隧道数据。
PKIX节点5个属性。
location:provider证书包的location。
passphrase:location的私钥启用密码,实际运营时,不应该填写该属性,而应该在服务器启动时输入。
trustsPath:信任证书路径, location中已经包含了派生该证书的ROOTCA, 如果要信任其它ROOTCA(或者当前ROOTCA即将过期,应该将新的ROOTCA放置在这里), 则需要配置trustsPath, trustsPath可以是一个文件, 也可以是一个目录, 如果是目录, 遍历一层, 获取文件, 文件允许任何证书格式, 包括CER, DER, PKCS7, PKCS12, KeyStore, 从中获取ROOTCA,忽略所有错误。
revocationCheckerOptions:服务器检测对方证书回收状态的选项,可选的值为DISABLE, NO_FALLBACK, ONLY_END_ENTITY, PREFER_CRLS, SOFT_FAIL, 大小写不敏感,后4个参数的解释见java.security.cert.PKIXRevocationChecker.Option,DISABLE具有最高优先级,如果配置了DISABLE,其它配置无意义。
httpsHost:指定key分发网络中特定的入口服务器IP或者域名,不配置该属性,使用provider证书中提供的域名。
PKIX节点配置初始化之后,从Provider证书中提取支持的信任域集合校验defaultGroup配置,如果配置了defaultGroup,defaultGroup必须在集合中存在,否则抛出运行时异常;如果没有配置defaultGroup,在集合中挑选一个作为defaultGroup。
不存在PKIX节点的情况下,使用SharedKey节点的配置,SharedKey节点允许多个,静态配置了信任域和相应的key。SharedKey配置方式不使用Key分发系统,用于调试环境,或者极其简单的应用场景。
SharedKey节点2个属性
group:信任域名称
key:信任域key
所有SharedKey配置初始化之后,生成了静态的信任域集合,如果配置了defaultGroup,defaultGroup必须在集合中存在,否则抛出运行时异常;如果没有配置defaultGroup,在集合中挑选一个作为defaultGroup。
Expire节点2个属性
group:信任域名称
value:过期时间
Expire节点允许精细配置特定信任域的隧道数据过期时间。
-
运行时注意事项
1. 运行ntp服务,同步provider时钟
2. 配置正确的dns服务。
3. 按照CA的要求,配置trustsPath,添加CA提供的额外ROOTCA。
4. 开启防火墙,许可443目的端口,确保与CAServer,Key分发网络的连通性。
-
-
5.8.6 高级话题
-
应用数据组织
1. Provider间数据交换框架不考虑具体的应用数据,专注于转发Octets
2. 信任域内Provider,应该设计私有的可交换数据结构,使用xml描述生成交换用的公共bean是理想方式。
3. 如果存在多种公共bean,应该设计类型字段予以区分,简单的情况下,可以考虑利用label区分类型。
4. 使用JSON表示用户数据也是一种可行的选择,JSON串上可以统一使用UTF8编码getBytes,然后wrap为Octets。
5. 应用数据尺寸可能较大的情况,配置压缩。
-
隧道数据尺寸
1. 隧道数据通过特定协议发送。
2. 出于服务器的抗攻击考虑, 虚拟机参数limax.net.Engine.limitProtocolSize, 限制了最大协议大小1M, 隧道数据尺寸不可超出这一约束, 否则, 目的服务器解析协议时将出错。
3. 客户端信任服务器,所以客户端不控制数据尺寸。
4. 对于大量数据,应该考虑多次发送的方式。
-
隧道谁的数据
1. 仔细规划应用自己的协商过程,决定隧道谁的数据。
2. 多数情况,通过隧道转发用户自己的数据。
3. 特定情况下,可以通过没有利害冲突的第三方传送。这时,真实的sessionid打包在应用数据中,解码获得该sessionid之后如果需要线程安全地处理,应该使用View.schedule(sessionid, task)将后续任务调度到sessionid对应线程上。
4. 出于规避防火墙的考虑,可以设计专用客户端,部署在连通性良好的网络中,转发 Provider间数据。
-