架构设计常画哪些图
在架构设计时经常需要画图。在大多数时候我们工作的团队并没有对架构设计中的图做规定只要能把架构表达清楚即可对于比较简单的软件甚至不需要画图只使用文字就可以描述清楚。这种因人而异的画图方式比较灵活并且效率高用较少的图就能表达较多的信息一张图中既能表达静态信息又能表达动态信息但是不利于跨团队之间的合作(很多时候也不需要跨团队合作当然也没有问题)。当需要跨团队合作或者多个人协同开发的时候统一画图标准往往就比较重要。uml定义了一些图的类型包括类图、组件图、时序图、流程图、状态机图、用例图等。不同类型的图适用的场景是不一样的类图、组件图用于表达软件的静态架构时序图用于表达不同软件组件的交互流程流程图适合表达一个组件的流程(聚焦于一个组件)状态机图用于描述具有多个状态的对象描述对象所具备的状态以及状态之间的转换关系转换条件用例图用于表示软件需求即站在用户的角度来看软件软件所具备的功能。在架构设计时往往需要用不同的图来说明软件的架构多种类型的图既有自己独特的部分也有相互重叠的部分从不同角度来共同描述软件的功能。架构设计的前置条件是软件需求软件需求往往包括功能需求性能需求接口需求这些都是站在用户的角度来提出的需求。功能需求满足用户需要的功能这是最基础的性能需求比如软件占用的内存、cpu不能大于多少完成一个功能的时间不能个大于多少接口需求如果用户对于接口有要求那么软件实现之后提供给用户的接口就要按照用户的需求来实现。架构设计的后置工作是详细设计或者代码开发这就要求架构设计要足够的清楚让代码开发人员能够按照架构设计进行代码开发。2静态图2.1组件图元素图用于表示组成软件的组件有哪些。比如对于一辆汽车有4大部分组成发动机、底盘、车身、电气设备。这4个系统可以看做是汽车的一级组成系统每个一级系统又会包括多个二级系统二级系统又会包括三级系统等等。这些都是汽车的静态组成部分。再比如linux操作系统由3个基础的子系统组成文件系统内存管理进程管理。另外设备驱动网络也是操作系统的重要组成部分。以linux的网络子系统为例其元素图如下下图进行了简化同时又能说明元素图的含义架构设计的前置条件是软件需求软件需求的主要内容就是功能需求也就是软件要实现什么样的功能。作为软件开发工程师首先要将功能进行拆分当然对于很简单的功能几行或者几百行代码就能实现这个时候功能比较单一也许不需要对功能进行拆分大部分时候我们开发的软件都是比较复杂的代码可以到几千行甚至几万行这个时候就需要对功能进行拆分。如上图所示linux网络子系统包括6个模块1epoll作为多路复用技术可以监听socket的事件比如socket收到了数据socket发生了错误2socket作为提供给用户的网络抽象可以代表一个tcp连接。socket和epoll均是用户可以使用的3tcp传输层协议面相连接的可靠的字节流。4ip网络层协议实现分片和路由5network devicelinux内核为网卡驱动提供的一套框架屏蔽掉不同网卡驱动的区别实现统一的接口。实现统一的接口这是软件设计中经常用到的思想封装一层提供统一的接口。6网卡驱动操作具体硬件2.2接口图接口图用来说明个功能模块之间的接口。各个模块功能实现软件的功能模块之间往往需要相互依赖。所在在架构设计时需要定义好模块之间的接口。定义好各个模块的接口之后在代码开发阶段可以分工合作每个人负责其中的一个模块。2.2.1epoll和socket之间的接口1socket提供接口sock_pollepoll调用该接口确定socket中发生了什么事件是有数据可读还是发生了错误还是发送缓冲区空闲了2epoll提供接口ep_poll_callback确切来说是epoll向socket注册了这个函数当socket中有事件的时候socket会调用到这个函数2.2.2socket与tcp之间的接口1tcp提供函数tcp_polltcp_recvmsgtcp_sendmsgsocket分别用来获取事件、接收数据、发送数据。2socket提供接口sock_def_readable、sock_def_write_space、socket_def_error_reporttcp分别用来向socket通知可读事件、写缓冲区空闲、报告错误。2.2.3tcp与ip之间的接口1接收数据时ip层收到报文之后通过函数tcp_v4_rcv将报文传递给tcp来处理2发送数据时tcp通过函数ip_queue_xmit将报文发送到ip层处理2.2.4ip与network device之间的接口1发送报文时ip调用函数__dev_queue_xmit发送报文2接收报文时network device调用ip_rcv将报文传递给ip层进行处理2.2.5network device和nic driver之间的接口network device把一些通用的从网卡驱动中抽象出来屏蔽掉不同网卡驱动的区别统一了接口。就是在ip层和网卡驱动层中间的一层。1接收报文时网卡驱动调用__napi_schedule触发软中断在软中断中处理接收到的报文2发送报文时net_device_ops.ndo_start_xmi挂的函数发送报文2.3类图类图能更详细的描述雷和类之间的关系每个类提供了哪些接口。uml类关系(实现、继承聚合、组合依赖、关联)_uml类关系 ea-CSDN博客3动态图时序图和流程图是最常用的两种动态图。时序图常用来描述模块和模块之间的交互关系。流程图常用来描述模块内部的流程。3.1时序图上图是最简单的接收报文的时序图。3.2流程图3.2.1函数tcp_sendmsg流程图1在tcp_sendmsg函数中首项要计算size_goal根据gso参数和mss计算size_goal。size_goal的意思发送的判断条件如果这次可以发送的数据已经不小于size_goal了那么可以立即发送数据。2将数据拷贝到skb中skb是贯穿整个网络协议栈的管理报文的数据结构。3如果用户数据均已拷贝到skb中则调用tcp_push来发送数据。tcp_push函数中并不是立即发送数据要进行autocork判断,自动阻塞。以下四个条件同时满足则会自动阻塞①当skb中数据长度小于size_goal②并且tcp_autocorking标志为true③重传队列不为空说明当有数据正在网络中传输并且ack没有返回可能很快就会有ack返回了ack返回的时候也会尝试发送数据。等待这很短的事件可能可以将用户的数据进行合并进行减少协议栈的调用次数。④当前发送队列中还有数据在等待发送之前的数据还在等待那么现在这个着急向下进行照样也是等待还不如在最开始的时候进行等待。static bool tcp_should_autocork(struct sock *sk, struct sk_buff *skb, int size_goal) { return skb-len size_goal READ_ONCE(sock_net(sk)-ipv4.sysctl_tcp_autocorking) !tcp_rtx_queue_empty(sk) refcount_read(sk-sk_wmem_alloc) skb-truesize; }3如果拷贝的数据长度不小于size_goal并且没有设置MSG_OOB标志判断force push标志,write_seq是拷贝到skb中的数据但是还没有发送pushed_seq是已经发送的数据当两者差值大于最大窗口的一半的时候则立即发送数据。static inline bool forced_push(const struct tcp_sock *tp) { return after(tp-write_seq, tp-pushed_seq (tp-max_window 1)); }当前skb正好是要处理的下一个skb立即发送。3.2.2函数tcp_write_xmit流程图