设计C/S架构应用程序的并发功能
C/S架构的ERP、CRM程序有的是以并发点(Concurrency)来销售,并发点是指同时在线人数。并发数量大时,理论上程序的运行速度会慢,软件供应商(vendor)也以控制并发的上限以解决客户对系统性能的抱怨。我接触到的一个ERP系统,它的定价策略如下表所示:
序号 | 并发用户 | 价格 |
1 | 5个以下 | 每用户20000,总价小于10万 |
2 | 5-20 | 每用户15000,总价小于30万 |
3 | 20-50 | 每用户12000,总价小于60万 |
4 | 50个以 | 每用户10000,总价最小50万 |
从软件开发的角度,我来分享一下我对并发功能的设计与实现。
需求与设计
1 正常的顺序是先启动服务器,再启动客户端主程序。如果启动客户端主程序时,连接不上服务器,要报错并终止程序。
2 运行过程中,服务器可能因各种情况停止工作。比如杀度软件扫描,停电等原因,这时我们的客户端主程序要能检测到服务器岩机,挂起当前界面。
为了减少这种事情发生的概率,我建议在服务器中安装程序AlwaysUp。
AlwaysUp能将可执行文件、批处理文件及快捷方式作为windows系统服务,并且进行管理和监视确保100%运行。当程序崩溃、挂起、弹出错误对话框时,AlwaysUp 能自动重启程序,并运行自定义的检查功能确保程序一直可用。AlwaysUp 能发送详细的email使你清楚地了解崩溃、重启等事件。
详细信息参考以下地址 http://www.0daydown.com/07/314246.html
3 我们的C/S程序有两种运行模式。第一种是客户端主程序与服务器不在同一台机器上,两个进程运行在物理隔离的两台电脑中,第二种就是客户端主程序与服务器都运行在服务器中,客户端以远程桌面的方式运行。
前一种模式好理解,两台机器之前以.NET通信机制(.NET Remoting,WCF)交互,后一种模式两个进程实际是运行在同一部电脑中,在并发控制上这两者有区别。
我们来看一下C#中的进程(Process)的定义,地址在
https://msdn.microsoft.com/en-us/library/system.diagnostics.process(v=vs.110).aspx
里面有一个SessionId的属性,它的含义如下
Gets the Terminal Services session identifier for the associated process. 获取进程的终端服务的会话标识。
在程序开发时为了识别是否是相同的并发,前者只需要根据IP地址或MAC地址,后者则需要根据SessionId来识别。
这个知识点的重要性在于,用户A已经登录过,在另一台电脑或会话中用户A再次登录时,系统要可以识别出来,要么阻止重复的登录,要么踢出前一个登录,要么刷新登录会话。
4 我们从数据的操作角度对并发用户作两个分组,一组是可编辑数据的用户,另一组是只读用户(readonly)。公司的主管,经理层或是总经理层,常常是查询报表,他们不需要操作数据。由于查询数据对服务器的压力要少很多(事务),所以一般在销售并发用户的时候,还会赠送相应数量的查看用户数。
5 用户之间关系的处理。管理员可以踢出用户,用户之间可以发送消息通知,管理员可以强制所有用户下线(由于系统需要进行重大更新,系统重要业务处理(月结,年结,期末处理等))。
6 运行过程中,客户端意外终止。比如一个耗费时间的操作(MRP运算,工作单发料,产品完工),用户在等待过程中失去耐心,强制杀死运行中的进程。这时因为没有调用Logoff方法清除服务器中的进程会话。如果再次启动登录时,可能会提示会话已经存在,或是登录用户超过最大许可数。
前面提到由于有心跳机制,服务器进程死去,客户端进程要挂起(阻止用户任何输入,暴力一点的方法是退出)。
这一点提到服务器运行正常,客户端意外终结,完全没有时机去通知服务器我已经下线。我们的处理方法是服务器每5分钟轮循一次客户端,检测到会话所在的客户端进程无法回应,则主动清除会话信息,以便于客户端下一次正常登录。
7 我们对许可机制有严格的要求,安装完成ERP后,会给当前机器环境生成一个签名文件,这个文件附注于许可文件中。运行时我们会检测当前运行的机器是否与许可文件中的机器签名匹配。
获取电脑配置可参考下面的方地:
private static string GetDiskDriveSignature(){
return WmiHelper.GetWmiPropertyValue("Win32_DiskDrive", "Signature");}
private static string GetDiskDriveSize(){
return WmiHelper.GetWmiPropertyValue("Win32_DiskDrive", "Size");}
private static string GetDiskDriveTotalTracks(){
return WmiHelper.GetWmiPropertyValue("Win32_DiskDrive", "TotalTracks");}
从代码中可以看出,是使用WMI。
这是服务器中的许可验证方法,客户端程序因为有并发数量控制,不验证许可文件和它的签名。
8 阻止服务器程序被第三方恶意API调用
.NET Remoting的服务端代码例子:
static void Main(string[] args){
TcpChannel channel = new TcpChannel(8080); ChannelServices.RegisterChannel(channel, false); RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingObjects.Person), "RemotingPersonService", WellKnownObjectMode.SingleCall); System.Console.WriteLine("Server:Press Enter key to exit"); System.Console.ReadLine();}
.NET Remoting客户端程序例子:
TcpChannel channel = new TcpChannel(); ChannelServices.RegisterChannel(channel, false); IPerson obj = (IPerson)Activator.GetObject(typeof(RemotingObjects.IPerson), "tcp://localhost:8080/RemotingPersonService"); string userName=obj.GetName();
最后一行我们调用了服务器中的程序。如果服务器程序被恶意人员获取,可以很容易的构造出客户端程序进行调用,服务器完全不知觉。为解决这个问题,我们的设计方案是客户端登录时,将当前环境因素(IP地址,电脑名,MAC地址,程序集版本与哈希值等)组合发送到服务器中,经过一个特定的算法,得出一个哈希值,登录完成后(用户名密码正确,权限允许)返回给客户端,客户端也以之前的环境变量进行算法计算,将这两者的值比较,若相等则允许登录。
这为恶意调用服务器程序增加了难度。
9 服务器会话
说穿不值一文钱,其实就是个DataTable对象,当有用户登录(Login)时,增加会话记录。用户注销(Logout)时,清除会画记录。
也可以学习ASP.NET的Session对象的设计思路,参考这里 Exploring Session in ASP.NET
DataTable因为操作上的不方便,后期维护的时候,我们把它完善成强类型对象。
[Serializable] public class Session{
public string SessionId { get;set;} public string UserId { get;set;} public string UserGroup { get;set;} public string MachineName { get;set;}......
}
public class SessionCollection :List<Session>{
}
//经过OOP的封装,调用时比DataTable要方便
Singleton<SessionCollection> sessions.....; sessions.Add(new Session());
10 日志记录
记录客户端登录日志,数据库表设计
1) 日志主表 UserLog(LogNo,LoginTime,LogoutTime,Profile)
纪录登入和注销时间,如果是客户端进程被强制杀死,LogoutTime常常是没有值。对于进程意外终止,用下面的方法不能截获终止前回调事件。
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CustomExceptionHandler.CurrentDomain_UnhandledException); Application.ThreadException += new ThreadExceptionEventHandler(eh.OnThreadException);
2) 日志明细表 UserLogDetail(LogNo,SeqNo,FunctionCode,OpenTime,CloseTime)
记录用户登录后,执行了哪些系统功能,持续了多长时间。
3 ) 日志数量表 UserLogDetailAction(LogNo,SeqNo,Remark)
记录用户登录后,操作了哪个功能的哪一笔数据。是做了编辑操作,还是执行过帐。Remark的算法如下
Entity salesOrder=... StringBuilder builder=new StringBuilder(); foreach(IField field in salesOrder.Fields){
if(field.IsPrmaryKey) builder.Append(field.Name+filed.CurrentValue);}
string remark=builder.ToString();
UserLog用户日志主表的最后一个字段Profiler,是一个后门,它记录了登录ERP系统的当前登录用户的本机电脑的几乎所有信息,相当于一个隐私收集工具。在审计(audit)的时候,我们可以用于帮忙用户澄清一些不必要的错误。
比如ERP的各部门主管常常是将ERP账户与密码给下面的同事,让他们帮忙获取数据,而自己常常是不进入系统的。
高一级的权限放开给不合理的人员,增加了系统的风险,而这个Profiler可以在一定程度上避免这种情况发生。
大公司的IT审计一看即可知道此登录的用户电脑不具备此高级权限。不过为了维护用户的声誉,我们对此功能做了选择性的处理,在实施时根据自己的实际需要去选择,默认情况下并不会进行隐私收集。
模拟测试
可以通过多开几个虚拟机来模拟测试并发,虚拟机与主机之前的连接方式如下:
1) 主机与虚拟机设为同一个网段的IP地址,比如192.168.1.100,192.168.1.101
2) 虚拟机与主机之间的网络连接方式设置为桥接(Bridge)
在测试并发时,可以将服务器端驻留在物理主机中,启动VS并开启调试模式。如果是以Windows服务存在,可以在程序中添加以下代码来强制附加调试器。
if(!Debugger.IsAttached) Debugger.Launch();
设计C/S架构应用程序的并发功能的更多相关文章
- 从一般分布式设计看HDFS设计思想与架构
要想深入学习HDFS就要先了解其设计思想和架构,这样才能继续深入使用HDFS或者深入研究源代码.懂得了"所以然"才能在实际使用中灵活运用.快速解决遇到的问题.下面这篇博文我们就先 ...
- 16套java架构师,高并发,高可用,高性能,集群,大型分布式电商项目实战视频教程
16套Java架构师,集群,高可用,高可扩展,高性能,高并发,性能优化,设计模式,数据结构,虚拟机,微服务架构,日志分析,工作流,Jvm,Dubbo ,Spring boot,Spring cloud ...
- 移动App设计之分层架构+MVC
http://www.cnblogs.com/Logen/archive/2012/11/08/2760638.html 场景分析:我们知道,一个移动设备的应用大多与网络有关,也就是说,我在移动设备上 ...
- .NET Core实战项目之CMS 第九章 设计篇-白话架构设计
前面两篇文章给大家介绍了我们实战的CMS系统的数据库设计,源码也已经上传到服务器上了.今天我们就好聊聊架构设计,在开始之前先给大家分享一下这几天我一直在听的<从零开始学架构>里面关于架构设 ...
- IM即时通讯:如何跳出传统思维来设计聊天室架构?
因为视频直播业务的大规模扩张,聊天室这种功能在最近几年又火了起来.本篇文章将会重点挑选聊天室这个典型场景,和大家分享一下网易云信在实现这个功能时是如何做架构设计的. 相关推荐阅读几十万人同时在线的直播 ...
- 从0开发3D引擎(十):使用领域驱动设计,从最小3D程序中提炼引擎(上)
目录 上一篇博文 下一篇博文 前置知识 回顾上文 最小3D程序完整代码地址 通用语言 将会在本文解决的不足之处 本文流程 解释本文使用的领域驱动设计的一些概念 本文的领域驱动设计选型 设计 引擎名 识 ...
- 从0开发3D引擎(十一):使用领域驱动设计,从最小3D程序中提炼引擎(第二部分)
目录 上一篇博文 本文流程 回顾上文 解释基本的操作 开始实现 准备 建立代码的文件夹结构,约定模块文件的命名规则 模块文件的命名原则 一级和二级文件夹 api_layer的文件夹 applicati ...
- TYPESDK手游聚合SDK服务端设计思路与架构之一:应用场景分析
TYPESDK 服务端设计思路与架构之一:应用场景分析 作为一个渠道SDK统一接入框架,TYPESDK从一开始,所面对的需求场景就是多款游戏,通过一个统一的SDK服务端,能够同时接入几十个甚至几百个各 ...
- SOA架构设计经验分享—架构、职责、数据一致性
阅读目录: 1.背景介绍 2.SOA的架构层次 2.1.应用服务(原子服务) 2.2.组合服务 2.3.业务服务(编排服务) 3.SOA化的重构 3.1.保留服务空间,为了将来服务的组合 4.运用DD ...
随机推荐
- 测试一下StringBuffer和StringBuilder及字面常量拼接三种字符串的效率
之前一篇里写过字符串常用类的三种方式<java中的字符串相关知识整理>,只不过这个只是分析并不知道他们之间会有多大的区别,或者所谓的StringBuffer能提升多少拼接效率呢?为此写个简 ...
- 在SQL2008查找某数据库中的列是否存在某个值
在SQL2008查找某数据库中的列是否存在某个值 --SQL2008查找某数据库中的列是否存在某个值 create proc spFind_Column_In_DB ( @type int,--类型: ...
- (JS+CSS)实现图片放大效果
代码很简单,在这里就不过多阐述,先上示例图: 实现过程: html部分代码很简单 <div id="outer"> <p>点击图片</p> &l ...
- 快速搭建springmvc+spring data jpa工程
一.前言 这里简单讲述一下如何快速使用springmvc和spring data jpa搭建后台开发工程,并提供了一个简单的demo作为参考. 二.创建maven工程 http://www.cnblo ...
- Angular企业级开发(1)-AngularJS简介
AngularJS介绍 AngularJS是一个功能完善的JavaScript前端框架,同时是基于MVC(Model-View-Controller理念的框架,使用它能够高效的开发桌面web app和 ...
- 浅谈web攻防
CSRF 跨站请求伪造(Cross-Site Request Forgery) -原理- 从上图可以看出,要完成一次CSRF攻击,受害者必须依次完成两个步骤: 1.登录受信任网站A,并在本地生成Coo ...
- 算法与数据结构(七) AOV网的拓扑排序
今天博客的内容依然与图有关,今天博客的主题是关于拓扑排序的.拓扑排序是基于AOV网的,关于AOV网的概念,我想引用下方这句话来介绍: AOV网:在现代化管理中,人们常用有向图来描述和分析一项工程的计划 ...
- Tesseract-OCR字符识别简介
OCR(Optical Character Recognition):光学字符识别,是指对图片文件中的文字进行分析识别,获取的过程.Tesseract:开源的OCR识别引擎,初期Tesseract引擎 ...
- Lind.DDD.Aspects通过Plugins实现方法的动态拦截~Lind里的AOP
回到目录 .Net MVC之所以发展的如些之好,一个很重要原因就是它公开了一组AOP的过滤器,即使用这些过滤器可以方便的拦截controller里的action,并注入我们自己的代码逻辑,向全局的异常 ...
- Consul-template的简单应用:配置中心,服务发现与健康监测
简介 Consul-template是Consul的一个方扩展工具,通过监听Consul中的数据可以动态修改一些配置文件,大家比较热衷于应用在Nginx,HAProxy上动态配置健康状态下的客户端反向 ...