本文的客户端基于我们的GQT开源项目:http://cxlh.iteye.com/blog/2021463
本人拙见,如有不同意见,欢迎拍砖,同时献给特别有对服务端跨语言需求的程序猿们!
客户端(GQT Demo V3(服务端配套版).rar)太大请挪步到QQ群下载(群号:101189702),注明:GQT或Java,C++等;
Java工程代码请挪步下载:http://cxlh.iteye.com/blog/2074307
总体设计思路:
- 高可用性:每个业务服务端都是独立的个体,任何一个业务服务器Crash时,都不会影响服务,并且业务服务器可以按需增加,业务服务端使用Java代码实现,主要是为了考虑更好的数据库操作,更好的事务支持,更好的社区支持以及可用更多的开源服务,提高开发效率;
- 自动负载均衡:当某个接口频繁调用,增加的响应服务器自动分流接口调用压力,也就说我们的异步的请求/响应模式下,有一个Broker仲裁程序决定客户端发来的请求交由哪个Java业务服务器来处理,这个Broker程序我们采用C++代码实现;
- 客户端(包括桌面或移动端)网关:根据策略选取远程Socket服务器连接,Socket服务器与服务器之间的消息通讯通过地址寻址远程路由,以便连在不同Socket服务器上的用户之间可以相互通讯,典型的应用就是聊天程序;
- Sub/Pub类消息:客户端订阅服务端的服务,服务端有多个Pub服务器(Java编写),交由Socket服务器将客户端Sub的消息Push给Client,此时Socket服务只作为Proxy用;
好了,其实这里只有2个C++核心组件(Socket服务器,Broker仲裁程序)和N个Java业务服务器(请求处理服务器,Pub服务器),以下介绍用法(需要一定的客户端编程知识和一些脚本只是):
客户端发送请求,异步收取,编写业务代码步骤:
编写Java服务端,比如我们编写一个按关键字读取股票列表等接口(依赖注入用的Spring):
@Repository @CacheNamespace(implementation = org.mybatis.caches.ehcache.EhcacheCache.class, readWrite = true) public interface StockDao { @Select("select SYMBOL,SHORT_NAME from `master`") public List<Map> listStock(); @Select("select SYMBOL,SHORT_NAME from `master` where SHORT_NAME like '%${k}%' or SPELL_NAME like '%${k}%' or SYMBOL like '%${k}%' limit 0,10") public List<Map> listStockByKeyword(Map<String,String> map); @Select("select SYMBOL,SHORT_NAME from `master` where exch_id=8") public List<Map> listAllSh(Map<String,String> map); @Select("select SYMBOL,SHORT_NAME from `master` where trade_date=#dt#") public List<Map> listStockByDate(Map<String,String> map); }
编写Java服务就这么简单:
package com.gqt.demo;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;
import java.util.ResourceBundle;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.gqt.server.BaseReqServer;
import com.gqt.server.ReqCallBack;
public class StockServer extends BaseReqServer {
private static Logger logger = LogManager.getLogger(StockServer.class.getName());
final static Properties prop = new Properties();
static{
InputStream is = null;
try {
String c_path = StockServer.class.getResource("/").getPath();
logger.info("c_path:{}",c_path);
is = new FileInputStream(new File(c_path+"config.properties"));
prop.load(is);
} catch (Exception e) {
e.printStackTrace();
}
}
public StockServer(String ip, String port, ReqCallBack callback) {
super(ip, port, callback);
}
public static void main(String[] args) {
logger.info("=============StockServer========");
new Thread(){
@Override
public void run() {
ReqCallBack callback = new StockCallBack();
String ip = prop.getProperty("gqt-reqserver-ip");
String port = prop.getProperty("gqt-reqserver-port");
final StockServer ss = new StockServer(ip,port,callback);
ss.startServer();
}
}.start();
}
}
编写客户端C++或脚本:
gw.req("命令代码"."命令代码对应的参数列表"); //回调函数 gw.s_cb_gw.connect(function(trcode,msg){ log("-----------------------------------------------------------------"); log("<p>gqt server异步方式获取数据,回调信息:trcode=" + trcode + ",msg=" + msg+"</p>"); });
效果如下:
这样做的好处:
- 桌面或移动的客户端再也不用编写繁琐的网络通信相关的程序,只需要发送请求/订阅,处理响应/订阅数据包即可,也不用关心底层的数据分包合包,客户端代理,网络传输的加解密和压缩等;
- 客户端开发人员也几乎不用和服务端程序员沟通,通过JSON解析请求透传给Java,Java响应的数据带上包头和响应给客户端,客户端解析JSON即可;
- 跨语言的服务端虽然会损失一定的性能,但似乎这点ZeroMQ已做的足够好;
Demo程序部署步骤:
- 创建一个MySQL Demo数据库,数据库名:stock,utf-8编码,导入output下的stock.sql;
- 配置output下的config.properties文件,指定机器IP,配置jdbc.properties,确保数据库配置正确;
- 点击runStockServer.bat,运行stock的Java Demo服务器,可以开任意个;
- 配置gqt-server-communicator(cpp)下的config_ims.ini文件,IP地址全部换成机器IP;
- 启动ss_server.exe,ss_zserver.exe;
- 点击客户端GQT程序,配置config_ims.ini中的IP地址,切换到gateway的标签页,enjoy it!