自己封装的Socket组件,实现服务端多进程共享Socket对象,协同处理客户端请求

摘要:
在实际项目中,发现单个进程服务器处理高度并发的客户请求的能力有限。因此,我一直在考虑多个进程是否可以像IISWeb Park一样提供一个服务来协同处理客户端请求。但如果使用,netframework的套接字组件似乎无法实现多进程共享。ConcreteTaskCreator似乎与SourceReaderService和OASystemService直接相关。如果ConcreteTaskCreator直接持有SourceReaderService和OASystemService对象,那么对它们进行单元测试是非常困难的。ConcreteTaskCreator保存ISourceReader和ITaskSystem接口的对象。通过这种方式,您可以测试ConcreteTaskCreator,或者通过传递不同的特定实现来实际运行产品代码。
自己封装的Socket组件,实现服务端多进程共享Socket对象,协同处理客户端请求

DotNet.Net.MySocketSLB.NET(Server Load Balance服务器负载均衡)项目中的核心组件。

在实际的项目中发现,单进程的服务端处理高并发的客户请求能力有限。

所以一直在考虑能不能象IIS Web园一样,多个进程提供一个服务,协同处理客户端请求。但如果用.net framework的socket组件似乎无法实现多进程共享。

于是就有了自己实现的想法。

下面是我写的一个demo程序(底部有下载

自己封装的Socket组件,实现服务端多进程共享Socket对象,协同处理客户端请求第1张

一、运程MySocket.Demo.Host.exe,启动了5个服务器程序

自己封装的Socket组件,实现服务端多进程共享Socket对象,协同处理客户端请求第2张

自己封装的Socket组件,实现服务端多进程共享Socket对象,协同处理客户端请求第3张

二、运行MySocket.Demo.Client.exe,启动客户端测试程序

自己封装的Socket组件,实现服务端多进程共享Socket对象,协同处理客户端请求第4张

三、查看服务器

自己封装的Socket组件,实现服务端多进程共享Socket对象,协同处理客户端请求第5张

demo下载

 
 
 
标签: Socket编写可维护代码的总结
 

前些日子写了个小项目,它即简单又有一定复杂度,正好可以用它来总结一下面向对象设计的一些原则和方法。

由于是公司的商业代码,不便透露,我这里就简单总结描述一下核心需求

简化版代码下载(VS2010,C#,无Web无Windows Service)

业务需求:

1. 有三个第三方的Web Service:SourceReaderService、OASystemService和OrderService

2. 监控从SourceReaderService获取的ReaderResults集合。筛选出符合条件的Result列表,根据这些符合条件的Result,通过OASystemService创建OATask

3. 等待人工确认新建的OATask是否需要创建Order

4. 可以手动在OA System中创建需要创建Order的OATask

5. 监控需要创建Order的OATask(包含手工创建的OATask),一旦发现存在需要创建Order的OATask,通过OrderService创建一条Order,并将Order ID写回相应的OATask;若该OATask为手动创建的,则需要将它添加到程序的日志记录中

6. 监控OrderService中OrderStatus有变化的Order,将OrderStatus更新回OATask

7. 若ReaderResult(从SourceReaderService获取回来的)在未完成的OATask中存在,不得创建新的OATask

8. 程序需要记录操作日志(Reader ID,Task ID,Task Create Date,Order ID,Order Create Date,Task Completed Date)

9. 使用Windows Service实现监控功能,此外还需要Web界面来查看日志,设置由ReaderResult创建OATask的触发条件,OATask的Subject、Assigned To等

三个Web Service我自己大体Mock了一下

clip_image002

下面是我写这个程序时的思路

业务较简单时的架构设计:透过表象看本质

当业务逻辑较简单时(比如本例),可以抛开一切细节来看程序的行为,而面向行为编程就等于面向接口编程,因为接口描述的就是行为!

本例中,表面上是三个Web Service之间的互操作,但是实际上该程序的核心业务应该至少包含三个行为:创建Task,创建Order和更新Task。照这个思路,创建三个相应的接口:ITaskCreator、IOrderCreator和ITaskUpdater。同时,它们的行为当然也自然而然地出来了:CreateTask()、CreateOrder()和UpdateTask()。

复制代码
    public interface ITaskCreator
    {
        void CreateTask();
    }

    public interface IOrderCreator
    {
        void CreateOrder();
    }

    public interface ITaskUpdater
    {
        void UpdateTask();
    }
复制代码

写出这三个接口之后,在我脑中就有了一个大概的构想:在一个Windows Service程序中使用三个Timer持有以上接口,然后在Timer的Elapsed事件中调用相应的接口方法就可以了

要写易于测试的代码

接口写出来后,该写具体实现了。

似乎ConcreteTaskCreator与SourceReaderService和OASystemService有直接关系,等等,如果ConcreteTaskCreator直接持有了SourceReaderService和OASystemService对象,那么对它进行单元测试异常地困难。本身第三方的Web Service就对测试不友好,将这些Web Service直接耦合到ConcreteTaskCreator中,使得ConcreteTaskCreator也变得不易测试了。

这个时候就需要将第三方Web Service封装起来了,具体思路如下图:

image

MockSourceReader专门为单元测试设计,而WebServiceSourceReader中则实际持有第三方API类(Web Service),它们实现了ISourceReader接口。

同理,其它两个Web Service也要使用接口封装:名称分别为ITaskSystem和IOrderSystem。

上面的做法实际上是Facade模式的应用。

ConcreteTaskCreator中持有的则是ISourceReader和ITaskSystem接口的对象,这样,可以通过传入不同的具体实现来对ConcreteTaskCreator进行测试或者实际运行产品代码。

image

同理,ConcreteOrderCreator和ConcreteTaskUpdater均持有ITaskSystem、IOrderSystem接口对象;对于手动创建的OATask,为了获取ReaderResult的信息以创建Order,ConcreteOrderCreator还需要持有ISourceReader接口对象。

为了记录日志,ConcreteTaskCreator、ConcreteOrderCreator和ConcreteTaskUpdater三个类都要持有数据持久层的对象。这个持久层对象也是使用和封装第三方Web Service相同的思路,通过接口来封装它,使得在运行单元测试时不使用真实的数据库(除非单元测试的目的就是要测试操作真实的数据库,否则在单元测试时使用真实的数据库会带来一系列问题,如测试运行速度慢、旧数据影响新测试等)。

本例是个简单的例子,无法体现当业务逻辑比较复杂无法立刻得到高层抽象时,写易于测试的代码有时会逼出更好的设计这个特点。

不要让第三方的类库过度污染你的程序

SourceReaderService返回ReaderResult,OASystemService中包含OATask和OATaskStatus,OrderService中包含Order和OrderStatus。以上这些都是第三方类库中我们必须用到的类。

如果在ISourceReader、ITaskSystem和IOrderSystem中直接使用这些类当参数或返回类型,那么,我们的高层抽象接口就会被第三方类库“绑架”。目前来看ITaskSystem使用的是OASystemService这个Web Service,如果变成另外一个第三方API而它需要的参数是XXTask类的实例的时候,被“绑架”的高层接口实际上已经无法与OASystemService分离了。所以,要限制第三方类库的“污染”范围,尤其是在第三方类库不稳定的情况下(不稳定的类库远比你想像的普遍,连微软的.Net Framework都有好多过时的方法、成员)

按照上面的想法,我们要创建自己的Task、Task Status、Order和ReaderData类(即使目前它们的属性和三个Web Service提供的类中属性可能完全一样),把主动权掌握在自己的手里!

数据库是实现细节!

它太重要了,以至于我要再重复一遍:数据库是实现细节!在项目设计时请不要先考虑数据库Schema

太多的程序因为在业务逻辑层掺进了数据库细节而使得业务层十分笨重!太多的程序因为在设计时是按照Database First的设计思想而使得业务层过多地关注持久层的细节从而导致业务逻辑混乱无法维护!这样的程序我自己也写过,也被坑过。

实际上,如果一个程序在持久层无论用哪一种方法(数据库、XML,甚至是内存对象)都不影响其它部分的运行,那么它应该是一个好的项目,至少在隔离持久层方面,它是好的。

本例的代码我不准备使用任何复杂的持久方法,只使用一个内存对象(IDbAccessor)就能让程序(或者是单元测试)运行起来。

博弈:代码量、代码复杂度与可维护性之间的平衡

单元测试无疑会增加代码量;

写出可维护的单元测试是一门大学问,这也会增加工作量;

可测试的程序无疑比不可测试的程序复杂度更高;

设计模式会增加复杂度,在滥用设计模式的情况下更甚;

好的抽象、封装和单元测试会大大增加代码的可维护性;

如何取舍?

我个人是这样认为的:

1. 如果一个项目要维护较长时间(个人认为一年半或两年以上就足够长了),好的单元测试必不可少

2. 好的抽象和封装水平因人而异,总之努力吧(这方面我水平也不高)

3. 使用设计模式要慎重。每当要应用一个设计模式时先问一下为什么要用?如果你明确地知道应用DP的代码在今后会产生变化(大多数DP的目的是封装变化点)或者为了让代码可测试,那么是应该应用DP的;如果是为了DP而DP(It’s cool,it’s 霸气酷狂屌!),或者是你根本不确定这一块代码今后会不会变化,那么,先做简单的封装也许会更好。

4. 如果Deadline很紧,要做的事情太多,根本没时间写单元测试或考虑抽象,《代码之殇》里有一节“向死亡进军”,好好看一下,免费的迷你书里面就有这一节。如果你改变不了这样的项目管理制度,也许你应该考虑换个老板吧

 
 
分类: .NetC#程序设计
标签: OOD程序设计

免责声明:文章转载自《自己封装的Socket组件,实现服务端多进程共享Socket对象,协同处理客户端请求》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇YOLOv3和YOLOv4长篇核心综述(上)Neo4j集群容器化部署下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

在控制台运行java时出错:错误: 找不到或无法加载主类

java源程序在eclipse里直接运行的话没有问题,但是在控制台时出现了错误。 1. 编译             编译没有出错,.class文件也正确显示的。 2. 运行             出错了,首先,我的环境是配置好的,也就是javac xxx.java,和java xxx之前是可以正常编译和运行的。那么这儿是什么问题呢? 3. 原因    ...

网络编程学习小结

几种网络编程方式: ISAPI、CGI、WinInet、Winsock 它们之间的差别: 1) ISAPI主要是开发基于浏览器client与server端程序。效率比CGI方式高,并且也扩展了CGI没有的一些功能。(基于TCP/IP模型中的应用层) 2) CGI主要是开发基于浏览器client与server端程序。(基于TCP/IP模型中的应用层...

打造数字化软件工厂 —— 一站式 DevOps 平台全景解读

本文为 CODING 协同产品总监张路宇 在腾讯云 CIF 工程效能峰会上所做的分享。文末可前往峰会官网,观看回放并下载 PPT。 欢迎各位朋友,来到腾讯云 CIF 工程效能峰会的分论坛,我是 CODING 的产品经理路宇,很高兴能以这种方式与大家见面。在接下来的主题环节中,会由我先为大家带来 CODING DevOps 研发平台的产品理念和全景解读,我们...

Java从入门到实战之(26)Java网络实例之练习

1、获取指定主机的IP地址 如何使用 InetAddress 类的 InetAddress.getByName() 方法来获取指定主机(网址)的IP地址? package Java_Learn.File; import java.net.InetAddress; import java.net.UnknownHostException; public...

SocketIO总结

我在马克飞象上写的一样的内容,感觉那个样式好看的:WorkerMan的部分总结 workerman中部分函数总结 以下是把我搜集到的资料进行了一个整合。详细怎么使用。慢慢摸索吧。 Worker类 中文文档 Connection类 中文文档 SocketIO类 中文文档 Socket类官网样例分析 在WEB消息推送框架官网中他介绍了一个简单的样例,说这...

FastAPI 学习之路(四十九)WebSockets(五)修复接口测试中的问题

其实代码没有问题,但是我们忽略了一点,就是我们在正常的开发中,肯定是遇到这样的情况,我们频繁的有客户端链接,断开链接,我们需要统一的管理起来,那么我们应该如何去管理呢,其实这个时候,我们要去声明一个类去管理我们的这些链接。我们应该如何优化呢。 定义一个链接管理类,处理我们所有的链接。 classConnectionManager: def __in...