空山雪林通用模块工作室

 

2014年5月30日

高可用性服务端的设计与实现

本文的客户端基于我们的GQT开源项目:http://cxlh.iteye.com/blog/2021463

 

本人拙见,如有不同意见,欢迎拍砖,同时献给特别有对服务端跨语言需求的程序猿们!

客户端(GQT Demo V3(服务端配套版).rar)太大请挪步到QQ群下载(群号:101189702),注明:GQT或Java,C++等;

Java工程代码请挪步下载:http://cxlh.iteye.com/blog/2074307 


总体设计思路: 

 

  1. 高可用性:每个业务服务端都是独立的个体,任何一个业务服务器Crash时,都不会影响服务,并且业务服务器可以按需增加,业务服务端使用Java代码实现,主要是为了考虑更好的数据库操作,更好的事务支持,更好的社区支持以及可用更多的开源服务,提高开发效率;
  2. 自动负载均衡:当某个接口频繁调用,增加的响应服务器自动分流接口调用压力,也就说我们的异步的请求/响应模式下,有一个Broker仲裁程序决定客户端发来的请求交由哪个Java业务服务器来处理,这个Broker程序我们采用C++代码实现;
  3. 客户端(包括桌面或移动端)网关:根据策略选取远程Socket服务器连接,Socket服务器与服务器之间的消息通讯通过地址寻址远程路由,以便连在不同Socket服务器上的用户之间可以相互通讯,典型的应用就是聊天程序;
  4. 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>"); });

 

效果如下:

 

 

 

 

这样做的好处:

 

  1. 桌面或移动的客户端再也不用编写繁琐的网络通信相关的程序,只需要发送请求/订阅,处理响应/订阅数据包即可,也不用关心底层的数据分包合包,客户端代理,网络传输的加解密和压缩等;
  2. 客户端开发人员也几乎不用和服务端程序员沟通,通过JSON解析请求透传给Java,Java响应的数据带上包头和响应给客户端,客户端解析JSON即可;
  3. 跨语言的服务端虽然会损失一定的性能,但似乎这点ZeroMQ已做的足够好;

 

Demo程序部署步骤:

 

  1. 创建一个MySQL Demo数据库,数据库名:stock,utf-8编码,导入output下的stock.sql;
  2. 配置output下的config.properties文件,指定机器IP,配置jdbc.properties,确保数据库配置正确;
  3. 点击runStockServer.bat,运行stock的Java Demo服务器,可以开任意个;
  4. 配置gqt-server-communicator(cpp)下的config_ims.ini文件,IP地址全部换成机器IP;
  5. 启动ss_server.exe,ss_zserver.exe;
  6. 点击客户端GQT程序,配置config_ims.ini中的IP地址,切换到gateway的标签页,enjoy it!


 

posted @ 2014-05-30 16:15 徐灵 阅读(1219) | 评论 (0)编辑 收藏

导航

友情链接

最新评论