设计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();

时间: 2024-10-13 01:02:19

设计C/S架构应用程序的并发功能的相关文章

几十万人同时在线的直播间聊天,如何设计服务端架构?

一个热门视频直播间人数可能达到几十万甚至上百万人,几十万人发消息,几十万人接收,流量相当惊人,那么服务端要如何设计才能保证系统流畅?本文作者将结合他在网易云信多年IM开发的经验进行深度分析. 推荐阅读 高并发IM系统架构优化实践 IM即时通讯:如何跳出传统思维来设计聊天室架构? 聊天室架构应满足哪些条件 高可用:任何一个节点故障都不应该引起服务不可用: 易扩展:具有水平扩展的特性,对不同量级的在线用户数都有应变的能力: 高并发低延迟:能支持大量的用户同时收发消息,消息从发出到送达所有在线端的延时

16套java架构师,高并发,高可用,高性能,集群,大型分布式电商项目实战视频教程

16套Java架构师,集群,高可用,高可扩展,高性能,高并发,性能优化,设计模式,数据结构,虚拟机,微服务架构,日志分析,工作流,Jvm,Dubbo ,Spring boot,Spring cloud, Redis,ActiveMQ,Nginx,Mycat,Netty,Jvm,Mecached,Nosql,Spring,大型分布式项目实战视频教程 视频课程包含: 高级Java架构师包含:架构师,高并发,分布式,集群,高可用,高可扩展,高性能,设计模式,数据结构算法,虚拟机,微服务架构,日志分析,

RNN-RBM for music composition 网络架构及程序解读

RNN(recurrent  neural network)是神经网络的一种,主要用于时序数据的分析,预测,分类等. RNN的general介绍请见下一篇文章<Deep learning From Image to Sequence>.本文针对对deep learning有一点基础(神经网络基本training原理,RBM结构及原理,简单时序模型)的小伙伴讲一下Bengio一个工作(RNNRBM)的原理和实现. 本文重点内容:针对RNN(recurrent neural network)一个应

移动App设计之分层架构+MVC(转)

场景分析:我们知道,一个移动设备的应用大多与网络有关,也就是说,我在移动设备上看到的数据,一般都是从Server上”拉“过来,显示在我们的移动设备(ios androiud.wpohone等)上.那我们就这个”拉“的过程分析,拉什么样的数据?去哪里拉?拉过来的数据怎么处理?用编程(开发)的思维看,就是定义什么实体(业务实体).发送请求.解析数据.当然这也只是大体的过程.但从软件架构设计上讲,定义实体.发送请求.解析数据都是具有单独意义的模块.那我们怎么处理这些模块呢? 场景应用:sina wei

云舒网络:容器系列二:容器的视角-设计交付和架构

前言: 我们要使用容器,自然就不能回避容器的设计生成过程,而生成之后就涉及到加载运行和管理,那么今天我们就从设计交付和架构这两个角度来说一下容器. 注:本期分享由代豪原创,云舒网络整理发布. 上篇的<容器起源>说了容器是什么,那么这次我们从两个视角谈谈容器.我们要使用容器,自然就不能回避容器的设计生成过程,而生成之后就涉及到加载运行和管理,那么今天我们就从设计交付和架构这两个角度来说一下容器.(注:由于目前容器中Docker应用比较广泛,以下内容中的容器默认为Docker.) 一.     设

SOA架构设计经验分享—架构、职责、数据一致性

阅读目录: 1.背景介绍 2.SOA的架构层次 2.1.应用服务(原子服务) 2.2.组合服务 2.3.业务服务(编排服务) 3.SOA化的重构 3.1.保留服务空间,为了将来服务的组合 4.运用DDD+GRASP进行分析和设计(防止主观的判断导致错误的假设) 5.SOA分布式下的数据一致性 5.1.分布式事务(基于DTC的分布式事务) 5.2.事务补偿(提供正向或反向的操作来让数据在业务上是一致的) 5.3.异步EDA(基于异步事件流来实现柔性的分布式事务) 6.总结 1.背景介绍 最近一段时

[转]SOA架构设计经验分享&mdash;架构、职责、数据一致性

阅读目录: 1.背景介绍 2.SOA的架构层次 2.1.应用服务(原子服务) 2.2.组合服务 2.3.业务服务(编排服务) 3.SOA化的重构 3.1.保留服务空间,为了将来服务的组合 4.运用DDD+GRASP进行分析和设计(防止主观的判断导致错误的假设) 5.SOA分布式下的数据一致性 5.1.分布式事务(基于DTC的分布式事务) 5.2.事务补偿(提供正向或反向的操作来让数据在业务上是一致的) 5.3.异步EDA(基于异步事件流来实现柔性的分布式事务) 6.总结 1.背景介绍 最近一段时

试解释下列名词:程序的顺序执行,程序的并发执行。

一个计算由若干个操作组成,若这些操作必须按照某种先后次序来执行,以保证操作的结果是正确的,则这类计算过程称为程序的顺序执行过程. 所谓的程序的并发执行是指若干个程序同时在系统中运行,这些程序的执行在时间上是重叠的,一个程序的执行尚未结束,另一个程序的执行已经开始. 补充: 顺序程序的特点: 顺序性.封闭性.可再现性. 并发程序的特点: 失去程序的封闭性.程序与计算不再一一对应.程序并发执行时的相互制约关系.

【转】关于B/S架构应用程序的权限设置分析和总结

来自:http://www.cnblogs.com/zhouxunyu/p/3790122.html 分析:不同的用户登录到系统后赋予不同的操作权限,而用户存在于数据库中,标识用户权限的字段也保存在数据库中的用户表中. 问题:这个标识用户权限的字段从数据库中取出如何保存在运行的程序中. 方案一:建立用户类,将从用户表查询出来的信息封装到类里面,用户登录时实例化类获取权限字段. 分析:该解决方案可行,弊端则是:当系统部署发布后同时在线访问用户量很大时,每次实例化的用户类会很多,服务器负荷会很大.