《企业应用架构模式》笔记(3)
上一篇(浅谈领域模型驱动中表的设计方法)算是读书笔记2。
这部分主要是说表现层和并发。
第四章 WEB 表现层
模型-视图-控制器(输入控制器)
控制器处理请求消息,模型负责领域逻辑,视图基于模型创建应当消息。
控制器
输入控制器和应用控制器
视图
三种模式:转换视图,模板视图和两步视图
两种选择:
1)使用转换视图还是模板视图。
模板视图:允许在网页的结构中编写表现层,并允许在网页中嵌入标签,用以标明网页中的动态内容导向到哪里。
优点:灵活性。
缺点:代码混淆,难以维护。
解决办法:使用服务器页面技术时,使程序的逻辑独立与网页。
转换视图:使用程序的一种转换风格。
如,XSLT
2)选择单阶视图还是选择两步视图。
单阶视图:为每个屏幕都准备一个视图组件
两步视图:把这一过程分层两个阶段,由领域逻辑产生一个逻辑屏幕,然后把他发送html网页中。其中每个屏幕都有一个第一阶视图,而整个程序中只有一个第二阶视图。
优点:可以全局改变html页面。
缺点:不容易提取逻辑屏幕结构。
如果web应用程序提供的服务由多种前端用户使用,运行两步视图更好。
输入控制器模型
两个责任:处理http请求消息,根据请求消息来决定下一步做什么。
两种模式:
1)为web站点的每一个页面都准备一个输入控制器。
2)把两个职责分开。
第五章 并发
软件开发中最辣手的问题。
用多线程或多进程操作同一数据,都会遇到并发问题。
离线并发:多数据库事务中数据操作的并发控制( 在跨事务的数据处理中管理并发问题)。
5.1并发问题
并发的本质问题:
更新丢失:
不一致行:读取两份各自正确的数据而在同一时间相互矛盾。
上面的问题会导致 正确性 的失败。但需要考虑灵活性。
5.2执行语境
请求和会话
从与外界交互的角度看,有两个重要的执行语境,请求和会话。
一个请求对应于软件工作的外部环境发出的单个调用。
一次会话是客户端和服务器端的之间一次长时间交互。
它可以是一个请求,但通常是由一系列用户认为逻辑上有关联的请求构成的。
一次会话通常从用户登录开始,然后用户进行各种操作。在会话结束的时候用户退出。
进程(process)和线程(thread)
两个操作系统的术语:进程(process)和线程(thread)
进程:通常是一个重量级的执行语境,将其正在处理的内部数据与外部隔离开。
线程:轻量级的活跃执行单元,一个单独的进程里面可以存在多个线程。
线程通常是共享内存的,会导致并发问题。
事务
处理数据库的时候另一重要语境,事务。
可以当多个请求当单个请求来看待。
5.3隔离和不变性
对企业应用来说,两个非常重要的解决方案:一个事隔离,一个是不变性。
隔离
并发问题发生在多个执行单元访问同一片数据的时候。使用隔离来划分数据,使得每一片数据只能被一个执行单元访问。操作系统为每个进程单独分配一片内存。
使用文件锁也是隔离。
不变性
识别那些不变的数据。
5.4乐观并发控制和悲观并发控制
当有一些可变数据无法隔离的时候。可以使用两种形式的并发控制策略。
例,personA和personB都要编辑文件File。
乐观并发控制:
都能得到一份文件的拷贝,并且可以自由编辑文件。第一个完成编辑的人可以直接保存文件。当第二个人开始提交修改时,并发策略开始起作用。检测两份修改的冲突,并拒绝提交。由第二个人指出怎样处理这种情况。
悲观并发控制:
只要有人先取出文件,其他人就不能对该文件进行编辑。
可以把乐观看成是冲突检测的,把悲观看成是冲突避免的。
比较:
悲观的问题是减少了并发的程度。
乐观更加自由一些,只有在提交的时候才有可能遇到阻碍。问题在于冲突的时候如何处理。
选择标准:冲突的频率和严重性
如果冲突很少,或者冲突的后果不严重,那么选择乐观锁。因为能够得到更好的并发。而且更容易实现。
5.4.1 避免不一致性
1)悲观锁策略通过读加锁和写加锁处理不一致问题。
共享锁(read lock,读锁)可以一次多个人对同一份数据加锁。但只要有人得到一个读锁,其他人就无法再得到写锁。
其他人能读不能写
排他锁(write lock,写锁)一旦有人得到写锁,其他人就不能得到两种锁中的任何一种。
其他人不能读也不能写
2)乐观策略将冲突检查建立在数据的某种标记上,可能是时间戳,也可能是顺序计数器。
3)时序读 也可以处理不一致问题。
每次读的时候根据使用时间戳等作为约束条件。
5.4.2 死锁
对于悲观技术有一个很特别的问题就是死锁。
解决办法:
1)超时控制
达到时间限制,锁失效。
2)检查机制
检查死锁,牺牲一方。
3)死锁的原因是人们在得到了一个锁的情况下还想得到更多的锁(或者是想从读锁升级为写锁)。因此防止的方法就是在开始的时候得到所有锁,之后不允许再得到锁。
4)可以硬性规定每个人获得锁的顺序。
5.5 事务 transaction
1)事务是一个有边界的工作序列,开始和结束都有明确定义。
2)所有相关资源在事务开始和结束时都保持一致。
3)每个事务要不全部完成,要不什么都不做。
5.5.1 ACID
软件事务经常使用ACID的属性来描述。
A 原子性 在一个事务中,动作序列的每一个步骤要么是全部成功,要么是全部回滚。
C 一致性 在事务的开始和结束的时候,系统的资源都必须处于一致的,没有被破坏。
I 隔离性 一个事务直到他成功提交以后,它的结果对任何其他的事务才是可见的。
D持久性 一个已提交事务的任何结果都必须是永久的。
5.5.2 事务资源
事务资源:可以进行事务处理的任何资源。
短事务:为了增加吞吐量,事务尽可能短。尽可能不要让事务跨越多个请求。
通常在请求开始时启动事务,请求结束时提交事务。
长事务:跨越多个请求的事务。
延迟事务:尽可能晚的打开事务,应在事务外完成读取数据的操作,只在修改数据的时候启动事务。
使用事务时,必须清楚的知道被锁住的到底是什么,数据库一般是锁住被访问的行。如果一个事务锁住许多行,数据库无法处理那么多锁,只能将行锁升级到锁住整个表。从而将其他事务锁在外面。
锁升级:这种锁升级对并发影响很大,这也是为什么不能在领域层超类级别上使用“对象”表的原因。这样很容易导致锁升级,而锁住该表后,其他对数据库的访问也就阻塞了。
这个说法第一次听到,确实让人茅塞顿开。
5.5.3 减少事务隔离以提高灵活性
四种隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 |
读未提交 | 是 | 是 | 是 |
读已提交 | 否 | 是 | 是 |
可重复读 | 否 | 否 | 是 |
可串行化 | 否 | 否 | 否 |
5.5.4 业务事务和系统事务
离线并发问题:自己为跨系统事务的业务事务提供ACID支持。
最容易解决的是原子性和持久性。最麻烦的隔离性(一致性)。
5.6离线并发控制的模式
1)乐观离线锁,在业务事务间使用乐观的并发机制。
缺点:只能在提交数据时才发现业务事务将要失败。
2)悲观离线锁,可以尽早的发现错误,但难以编程实现,而且会降低灵活度。
5.7应用服务器并发
应用服务器自身的进程并发。
处理办法:
1)每会话一进程,各个进程之间完全隔离,问题是大量资源的消耗。
可以使用进程池来提高利用率。
2)使用进程池的每会话一进程方式能使用更少的资源处理同样多的会话。
3)每会话一线程,最重要的是创建和进入一个隔离区,最常用的方法是让线程每次都创建新的对象来处理请求,以保证这些对象不被其他线程访问(比如 静态变量)。