2010年9月28日
本文的客户端基于我们的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!
该项目包含的通用模块代码等我有时间一并剥离贡献出来(基于WebSocket的通知引擎,工作流整合模块,自定义表单(详见这里),基于RBAC权限设计),最近太忙了,Web项目有一段时间没碰,有点生疏的感觉,主要在忙GQT项目,一套基于桌面开发的框架,详见这里,写代码写的有点手酸的感觉。
基于Web的含工作流的项目看起来并不如想象的那么简单,主要需求:
- 灵活定制工作流,并跟踪流程进度;
- 每个Order含有历史轨迹记录,可在历史中查看;
- 工作流的Action灵活,认领任务不一定非要先提取表单,因为很多节点都只有几个动作,直接按钮操作即可;
- 待办事宜列表在不刷新页面情况下也能变动;
项目要求:
- 操作简单高效;
- 权限细节到按钮级别;
- 并发数少,不超过3000个在线用户;
主要可能使用到技术:
- 工作流引擎,我这里选用Activiti5,很灵活好用;
- 权限使用Spring Security,基于标签式管理权限很方便;
- 通知引擎使用WebSocket,基于Flash实时通信,基于socket.io;
- 权限粒度基于经典的RBAC;
- 总体框架Spring MVC+Mybatis;
实现的WebSocket的总体思路:
- WebSocket Server独立于Web项目,Web Server与WebSocket Server之间的局域网通信基于简单的Socket通信,这样这个组件可以完全解耦和通用;
- 当Web项目要Push消息到Client时,通过Web Server的Socket Client向WebSocket Server的Socker Server发送消息,然后WebSocket Server收到消息后解码,广播到所有浏览器;
我们实现的事件通知非常简单,设定全局变量并让浏览器侦听:
var G_WebSocket=false;
var EVENT_ORDER_CHANGE_STATUS = "orderChange";
var EVENT_ORDER_CHANGE_AMOUNT = "amountChange";
var EVENT_ORDER_CHANGE_REFUND = "refundChange";
WebSocket.init = function(callbackFunc){
socket = io.connect(connUrl, connOptions);
socket.on('connect', function() {
G_WebSocket=true;
callbackFunc("connect",null);
});
socket.on('disconnect', function() {
G_WebSocket=false;
callbackFunc("disconnect",null);
});
socket.on('clientQuit', function(obj){
G_WebSocket=false;
callbackFunc("clientQuit",obj);
});
socket.on('broadcast', function(obj) {
callbackFunc("broadcast",obj);
});
};
在需要侦听WebSocket接受Web Server推送消息的地方加上一个函数即可:
WebSocket.init(function(command,jsonObj){
if(command=="broadcast"){
if(jsonObj.e == EVENT_ORDER_CHANGE_STATUS){
//TODO:write your code here
}else if(jsonObj.e == EVENT_ORDER_CHANGE_AMOUNT){
//TODO:write your code here
}else if(jsonObj.e == EVENT_ORDER_CHANGE_REFUND){
//TODO:write your code here
}
}
});
这样的结构要扩展推送服务很简单,比如按频道推送等,都可以很容易的扩展。
再看看看工作流,我们实现了activiti通用的申请提交任务流程和自定义表单功能,提取跟踪流程图功能等,这样你要设计一个新流程也变得非常简单,只需要在eclipse里划上工作流图,在后台发布,然后通过SpringMVC的RestAPI启动实例流程,申领完成任务等,如下图:
流程走到了分支的两个节点上,这样对后续新增的工作流提供了极大的遍历。
最后说说Spring Security,基于RBAC的权限体系搭建好后(可以用在任何管理系统中),要在页面中访问一个资源,首先判断一下是否有权限,如下HTML:
<sec:authorize ifAllGranted="r_pd">
<a href="#">resource access here</a>
</sec:authorize>
<sec:authorize url="/XXX/XXX/XXX.html">
<a href="XXX/XXX/XXX.html'">
<span>XXX功能</span>
</a>
</sec:authorize>
前台由于项目比较小,没有用到js的MVC框架,如backbone等,这里就不再记录了。
偶玩的版本是3.3.1,简单而华丽的外表,界面截图如下:
安装FD,你首先需要安装:
- JDK 1.6以上版本
- .Net运行环境
继而你就可以一路Next完成安装了……
安装完成后,你必须去adobe官方网站下载开源的Flex SDK,我这边下载的版本是:flex_sdk_3.5.0.12683_mpl
下载完成后,解压到某个目录下即可,如:F:\flex_sdk_3.5.0.12683_mpl
然后在FD中设置Flex SDK,如下图:
我们这边以Open Flash Chart为例,导入项目工程,点击运行,如下图:
你可以设置断点,运行表达式,使用起来非常方便,强烈推荐给大家使用!
已有 1 人发表留言,猛击->>这里<<-参与讨论
JavaEye推荐
10月5,6日去宁波附近的度假村旅游了一下,一家3人出发了,由于儿子还比较小,怕他累着,所以这次就去了九龙湖开元名都大酒店度假村,5星级的酒店,住了一晚,有点小贵,协议价后也得880元一晚,不过是无敌湖景房,超大阳台,阳台上就可以看到整个九龙湖,甚是壮观,儿子玩的开心的不得了,在酒店的花园里滑滑板车,5日早上出发,6日中午回来,在回来的路上,顺便小逛了一下慈城古镇,到宁波吃了点饭,到家就该儿子的睡觉时间了……这个国庆节3日就简单的陪儿子在东钱湖上座了下船,他很喜欢坐船的……其实时间都在不知不觉中度过,国庆长假结束了……
PS:附上酒店小图一张,以之纪念……
已有 0 人发表留言,猛击->>这里<<-参与讨论
JavaEye推荐
最近项目要用到Rest,选择了Spring 3,关于Rest的介绍:
REST关键原则
REST定义了应该如何正确地使用(这和大多数人的实际使用方式有很大不同)Web标准,例如HTTP和URI。如果你在设计应用程序时能坚持REST原则,那就预示着你将会得到一个使用了优质Web架构(这将让你受益)的系统。总之,五条关键原则列举如下:
- 为所有“事物”定义ID
- 将所有事物链接在一起
- 使用标准方法
- 资源多重表述
- 无状态通信
Spring 3.0开始将全面支持Rest,而且配置实现起来也相当简单,利用Spring MVC在web.xml定义片段:
<servlet>
<servlet-name>mydemo</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mydemo</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
继而在Web-INF目录下增加mydemo-servlet.xml,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- Auto scan, declare the location path -->
<context:component-scan base-package="com.mydemo.springmvc.rest" />
<!-- Using annontation -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<!-- Resolve the view, declare the prefix and suffix -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/view/" p:suffix=".jsp" p:viewClass="org.springframework.web.servlet.view.JstlView" />
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
p:defaultEncoding="utf-8" />
</beans>
这里我们定义了JSP文件的标签库用jstl,并启动了注解功能,并自动扫描com.mydemo.springmvc.rest下的controller,接着我们就可以在com.mydemo.springmvc.rest包下编写controller代码
并可以使用注解功能,如类似的代码如下:
@Controller
public class MyDemoController {
private CommonDAO commonDAO;
@Autowired
public void setCommonDAO(CommonDAO commonDAO) {
this.commonDAO = commonDAO;
}
private MyDemoController (){}
@RequestMapping(value="/home", method=RequestMethod.GET)
public String welcome(){
return "/home";
}
}
然后就可以在view目录下通过增加home.jsp来实现显示层代码的编写工作,所有的步骤就以上这些,接下去你就可以通过类似:http://localhost:8080/mydemo/home,来访问了(这里Controller通过注解的方式注入DAO以便进行数据库的访问)
已有 0 人发表留言,猛击->>这里<<-参与讨论
JavaEye推荐