浅谈网络客户端的类库编写和使用

简介: 我们知道TCP连接的代价是比较大的,因此很多时候我们都会使用长连接,对于客户端也就会使用连接池。 而各种客户端实现的方式不尽相同,API的最佳使用方式也不尽相同,如果使用不当则会发生很大的问题。 比如,在每次连接服务端的时候都初始化连接池,每次使用之后释放连接池,那么会导致很可怕的性能问题。

我们知道TCP连接的代价是比较大的,因此很多时候我们都会使用长连接,对于客户端也就会使用连接池。

而各种客户端实现的方式不尽相同,API的最佳使用方式也不尽相同,如果使用不当则会发生很大的问题。

比如,在每次连接服务端的时候都初始化连接池,每次使用之后释放连接池,那么会导致很可怕的性能问题。

又比如,在每次用完连接池中的连接后没有能把连接归还,那么连接池会认为这个连接在使用,从而创建越来越多的连接。

其实很多时候用户对于类库的使用不当也体现了类库的设计不当。

现在我们来看几个不太好的设计:

1) 某个应用提供了一个Client类下面提供了若干API,比如:

Client client = new Client();

Database db = client.GetDatabase();

db.Open();

db.Add();

但用户不知道Client构造的时候会初始化一个连接池,这样相当于没一次调用创建10个连接,然后废弃。

效率极差,其实Client类提供了静态入口Instance,但是Client没有关闭构造方法,导致了这个问题。

在private构造方法之后,就只能通过Client client = Client.Instance来获取了,避免了这个问题。

2) 还是这个应用,它为Client类提供了Disconnect()方法,开发人员这么使用:

Client client = Client.Instance;

Database db = client.GetDatabase();

db.Open();

db.Add();

client.Disconnect();

而在Disconnect内部其实是释放了连接池,在Add方法中判断如果没有可用连接则创建。

那么这样也就相当于每次都会创建10个连接,性能同样非常差。

其实Client类不应该有Disconnect实例方法。注释了最后一行后问题解决。

(该释放的不释放,不该释放的释放了)

3) 即使这样还有问题,在调用Add方法之后,系统抛出异常,不能对未打开的数据库执行操作。

开发人员很容易想到去调用Open或Connect之类的方法。

但是最后却忘记调用Close()了,由于连接池设置了10000的上线,并且启用了5分钟空闲就回收的机制。

所以系统一开始上线并没有出现问题,但是运行一段时间之后时不时会出现连接池满的问题。

在db.Add()之后调用db.Close()解决问题。

其实,类库的设计人员在在Add方法内部进行连接的获取和回收那多好啊?

即使不知道操作的边界,也应该提供一个IDisposable的Database类。

那么现在就可以回答这些问题了:

1) 连接池的初始化方法应该怎么实现?是静态方法还是实例方法?

宗旨:连接池相对唯一,绝不能每次使用创建,每次用完关闭。

个人认为,不提供连接池的初始方法 > 静态方法 > 实例方法。

因为连接池本来就是一个内部的实现,作为类库的调用放本不应该知道怎么去使用。

在万不得已的情况下,API入口方法也应该以静态方法提供。

因为如果提供了非静态的方法,API的使用者很可能会尝试调用Close之类的Dispose方法去关闭连接池,释放所有连接。

(当然,不应该提供或谨慎提供连接池的Dispose是后话了)

2) 连接池的重置方法应该怎么实现?

个人认为最好不要提供,如果一定要提供那么就在单独的辅助方法中提供,避免误调用。

3) 涉及到操作的方法应该怎么实现?IDisposable?

涉及到操作的方法往往关联了连接的获取和释放。对于使用连接池的实践来说,应该尽早返回,尽量晚获取。

可以实现IDisposable要求开发人员确保连接在用完之后返回。

个人认为最好的方式还是直接提供静态API,在内部包装连接的获取和返回。

避免万一调用者没有Dispose的情况,对于特殊情况比如要求持续使用一个连接可以考虑创建一个IDisposable的OperationScope的类。

作为类库的编写者不但应该在设计类库上尽量考虑调用者的习惯和方便而且也应该在类库文档中提供最佳实践,避免一些悲剧的发生。

随着越来越多的开源服务端出现,开源的客户端也越来越多了,但是其中的实现各不相同,在使用之前还是应该大致了解使用实践,毕竟不是所有的开源客户端的API都像微软那样人性化的。

作者: lovecindywang
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
8月前
|
存储 Python
Python网络编程基础(Socket编程)UDP客户端编程
【4月更文挑战第9天】在UDP通信中,客户端负责发送数据到服务器,并接收来自服务器的响应。与服务器不同,客户端通常不需要绑定到特定的地址和端口,因为它可以临时使用任何可用的端口来发送数据。下面,我们将详细讲解UDP客户端编程的基本步骤。
【计算机网络】如何让客户端构造一个HTTP请求-2
【计算机网络】如何让客户端构造一个HTTP请求-2
【计算机网络】如何让客户端构造一个HTTP请求-2
|
8月前
|
存储 前端开发 JavaScript
【计算机网络】如何让客户端构造一个HTTP请求-1
【计算机网络】如何让客户端构造一个HTTP请求-1
|
3月前
|
网络协议 Java API
【网络】TCP回显服务器和客户端的构造,以及相关bug解决方法
【网络】TCP回显服务器和客户端的构造,以及相关bug解决方法
84 2
|
3月前
|
存储 网络协议 Java
【网络】UDP回显服务器和客户端的构造,以及连接流程
【网络】UDP回显服务器和客户端的构造,以及连接流程
74 2
|
3月前
|
安全 区块链 数据库
|
4月前
|
网络协议 C语言
C语言 网络编程(十)TCP通信创建流程---客户端
在TCP通信中,客户端需通过一系列步骤与服务器建立连接并进行数据传输。首先使用 `socket()` 函数创建一个流式套接字,然后通过 `connect()` 函数连接服务器。连接成功后,可以使用 `send()` 和 `recv()` 函数进行数据发送和接收。最后展示了一个完整的客户端示例代码,实现了与服务器的通信过程。
|
5月前
|
API Windows
揭秘网络通信的魔法:Win32多线程技术如何让服务器化身超级英雄,同时与成千上万客户端对话!
【8月更文挑战第16天】在网络编程中,客户/服务器模型让客户端向服务器发送请求并接收响应。Win32 API支持在Windows上构建此类应用。首先要初始化网络环境并通过`socket`函数创建套接字。服务器需绑定地址和端口,使用`bind`和`listen`函数准备接收连接。对每个客户端调用`accept`函数并在新线程中处理。客户端则通过`connect`建立连接,双方可通过`send`和`recv`交换数据。多线程提升服务器处理能力,确保高效响应。
61 6
|
5月前
|
网络协议 C# 开发者
WPF与Socket编程的完美邂逅:打造流畅网络通信体验——从客户端到服务器端,手把手教你实现基于Socket的实时数据交换
【8月更文挑战第31天】网络通信在现代应用中至关重要,Socket编程作为其实现基础,即便在主要用于桌面应用的Windows Presentation Foundation(WPF)中也发挥着重要作用。本文通过最佳实践,详细介绍如何在WPF应用中利用Socket实现网络通信,包括创建WPF项目、设计用户界面、实现Socket通信逻辑及搭建简单服务器端的全过程。具体步骤涵盖从UI设计到前后端交互的各个环节,并附有详尽示例代码,助力WPF开发者掌握这一关键技术,拓展应用程序的功能与实用性。
186 0
|
5月前
|
存储 网络协议 物联网
网络中的“客户端”和“服务器
【8月更文挑战第24天】
255 0