专栏首页高性能分布式系统设计基于 CSP 的设计思想和 OOP 设计思想的异同

基于 CSP 的设计思想和 OOP 设计思想的异同

Go语言推崇的CSP编程模型和设计思想,并没有引起很多Go开发者包括Go标准库作者的重视。标准库的很多设计保留了很浓的OOP的味道。本篇Blog想比较下从设计的角度看,CSP和OOP到底有什么区别。

下面,我们来看一个例子,如果我们有一个项目,需要做一个TCP连接中继器(请原谅我的用词)。我们先按照OOP来设计下:

  1. 系统的结构:需要有一个客户端和一个服务器端。分两个进程分别跑在不同机器上。
  2. 系统对象关系拆分(这里有所简化,E-R图等省略):连接中继器类--系统的主类config类--描述配置的类、connection类--每个连接一个conn类的实例、pipe类--提供一个管道,把上游的连接和下游的连接打通,把数据从A--pipe--B、encrypt工具类,提供各种加解密工具。
  3. 理清楚系统中各种对象(类)的作用关系,设计接口的细节。这里的接口,其实就是对象之间相互发送的同步阻塞的消息。
  4. 设计错误处理,日志等。
  5. 从性能方面审视整个设计,优化。

=====

好,我们再按CSP的思路来设计下,是这么一个过程:

  1. 我们需要一个 main 协程来处理各种命令行参数的配置,收集处理配置文件; 如果是server,那么初始化server的主协程 -- tcpRemote;如果是client,则启动client的主协程 -- tcpLocal;
  2. 分别设计 tcpRemote和tcpLocal。tcpRemote 是server主协程,负责在指定的端口监听,如果有新的连接连入,则启动一个协程处理 -- serverConn ; tcpLocal是client的主协程,负责在客户机的指定端口监听,如果有连入连接,则启动一个协程处理 -- clientConn;
  3. 分别设计 serverConn和clientConn。serverConn 处理新连入连接的请求,把连接交给shadow函数处理得到一个装饰后的连接,析出目的Addr,发起连接到远端,再交给 relay 函数处理。clientConn 处理新连入的连接请求,析出目的地址, 发起一个新的到server的连接,把连接交给shadow函数处理得到一个装饰后的连接,转发请求给server,接下来的工作交给 relay 函数来处理。
  4. shadow函数,接受一个连接返回一个连接,标准的装饰器。由于需要实现多种加密算法,所以shadow函数有很多实现。程序启动时,在初始化阶段根据配置,注册好对应的shadow函数实现。
  5. relay函数,一个桥接器,接受两个连接,异步转发两个连接中 上行 和 下行 的数据。分别用两个协程来实现数据的 上行 和 下行 的转发逻辑。
  6. 设计错误处理,日志等。
  7. 从性能方面审视整个设计,优化。

=====

OOP部分写的比较简略,但是设计思路还是能看出来的,OOP的设计 核心的围绕点是系统中的对象的种类、职责以及相互的关系;OOP在低并发的时代诞生,对于系统中动力分配是不怎么重视的。在遇到具有共性的点的时候,OOP多是用接口的形式表达,多个不同类实现同一个接口。

CSP的设计 核心的围绕点,是系统中的动力源,也就是系统中动力的来源。动力源在Go语言里就是goroutine;由于goroutine往往是通过闭包函数创建出来,所以闭包函数捕获的upvalue等,也就成了父goroutine和子goroutine之间的一种隐藏的协议。更重要的,每个协程,本质上就是在调度发生时,自动把 上下文 保存起来的回调函数。这大大简化了状态的维护工作。在遇到具有共性的点的时候,CSP是多用装饰器和桥接器,把系统中的共性用函数的参数表达出来。

这两种设计中,接口和函数其实可以相互转换,一个接口只有一个方法的等价于一个函数;而几个函数构成了固定的组合,在Go里面等价于实现了某个接口。所以,这种对共性抽象的方法并没有太大的差别,甚至有人就推崇在Java中,一个接口就只有一个方法。

=====

OOP、FP、CSP、Actor等思想,其实都是在做取舍,究竟要隐藏那些细节暴露那些功能。如果什么都不考虑,那就是汇编了(近似的说法)。没有最优的设计思想只有合适的设计思想。

无论OOP/FP/CSP/Actor模型,都是可以相互转换、替换和实现。

FP/CSP/Actor中大量用闭包,其实就是把OOP的结构体交给编译器去自动生成而已,每个闭包函数捕获的upvalues在各种支持闭包的语言中,多是交给编译生成一个特殊命名的结构体,并在闭包传递时一并生成实例并传递引用。这样就使得一些地方用于消息传递的结构体可以省略,很多时候在chan中传递一个func()比传递一个消息更加的方便。Go的chan可以看作是把传统OOP语言以 方法调用形式 表达的同步阻塞消息传递,改成了显式的消息传递,更好的是,多路分发和逆多路分发机制也集成在语言中。

OOP中的方法调用本质上就是一种同步阻塞的消息传递,这点在ObjC中表现的非常清楚,ObjC中每个方法调用本质上就是发送了一条消息给某对象(sendmsg是一个变参数函数)。OOP隐形约定了,所有的进程内的语言运行时级别的消息传递都是同步阻塞的。而Go/Erlang等CSP/Actor模型的语言,打破了这一点,提供了语言级别的异步非阻塞的消息传递。

如果我们把软件设计分成 装配、动力驱动、可变性 三个方面考虑。

OOP的装配工作量比CSP要大的多。每个接口的每个方法都可以看成是一个螺丝,只有你紧固了每一个螺丝,OOP设计的软件才可以运行。而CSP设计的程序,每一个协程的创建,都是一个装配点,仰赖方便的闭包机制,装配所需螺丝是一次性自动紧固的。这就是CSP在设计上的优势之一吧。

在动力驱动方面,OOP由于假设了方法调用是同步阻塞的消息传递,其动力驱动也比较原始,大部分是依赖操作系统提供的线程和进程机制。但是CSP则提供了异步的非阻塞消息机制,以及自动上下文保存的可中断函数(也就是协程)。这些机制使得CSP的动力驱动简单高效。

在可变性方面,OOP的合约是由接口和结构体来约束的,而CSP的合约是由函数签名和闭包的upvalues来约束的。函数的参数和返回值可以都是空,只用upvalues来隐式表达约束。因此CSP在可变性方面也是更优秀的。

P.S.

需要强调的是OOP并没有什么特别的不好的,相反OOP具有巨大的优势,就是容易设计。

CSP虽然会要求从设计上改变即有思路,耗费较多的脑力,但其设计方案简单容易扩展,具有巨大的优势。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【GO语言】合理配置GOMAXPROCS提升一倍以上的性能

    GOMAXPROCS 用默认的,就是CPU的硬件线程数目, 对于大部分File IO密集的应用是不合适的。 至少应该配置到硬件线程数目的5倍以上, 最大1024...

    Linker
  • 写了一个Java like的ExecutorService的Go语言实现

    练习下刚刚学的Go语言。GoExecutors 主要用法可以看main.go文件。 主要是想用Java处理并发的ExecutorService系列工具类的模式,...

    Linker
  • Clojure 启动出错

    X:\clojure-1.3.0>java -cp clojure.jar main Exception in thread "main" java.lang....

    Linker
  • 前端防御从入门到弃坑——CSP变迁

    作者:LoRexxar'@知道创宇404实验室 0x01 前端防御的开始 对于一个基本的XSS漏洞页面,它发生的原因往往是从用户输入的数据到输出没有有效的过...

    Seebug漏洞平台
  • 前端防御从入门到弃坑——CSP变迁

    对于一个基本的XSS漏洞页面,它发生的原因往往是从用户输入的数据到输出没有有效的过滤,就比如下面的这个范例代码。

    Seebug漏洞平台
  • 打造属于自己的博客app——基于react native和博客园接口

    关注react native这个技术很久了,去年就做了一个简单的Demo,最近有时间,重新了解了一下react native的现状,发现已经有很大的进步,现在完...

    itmifen
  • Matlab系列之函数参数

    不知道你们是否有等待今天这篇的到来,这篇其中要讲到的函数参数,是个好东西,但是感觉初学的时候总会有点懵逼,希望你们能理解讲的东西,如果不理解,那就多看几遍吧,我...

    狂人V
  • LeetCode 94:二叉树的中序遍历 Binary Tree Inorder Traversal

    Given a binary tree, return the inorder traversal of its nodes' values.

    爱写bug
  • 《闲扯Redis三》Redis五种数据类型之List型

    Redis 中的 list 是我们经常使用到的一种数据类型,根据使用方式的不同,可以应用到很多场景中。

    大道七哥
  • 《闲扯Redis三》Redis五种数据类型之List型

    Redis 中的 list 是我们经常使用到的一种数据类型,根据使用方式的不同,可以应用到很多场景中。

    大道七哥

扫码关注云+社区

领取腾讯云代金券