Tomcat容器的责任链模式

image-20201112212937756

主要流程总结为:

  1. 请求被Connector组件接收,创建Request和Response对象;
  2. Connector将Request和Response交给Container,先通过Engine的Pipeline组件流经内部的每个Valve;
  3. 请求流转到Host的Pipeline组件中,并且经过内部Valve的过滤;
  4. 请求流转到Context的Pipeline组件中,并且经过内部的Valve的过滤;
  5. 请求流转到Wrapper的Pipeline组件中,并且经过内部的Valve的过滤;
  6. Wrapper内部的WrapperValve创建FilterChain实例,调用指定的Servlet实例处理请求;
  7. 返回Request和Response对象。

Tomcat两大组件

Tomcat两大组件包括:

  • Connector组件:连接器,主要负责客户端与Tomcat的通讯;
  • Container组件:Servlet容器。

![image-20201112211136090](2020-11-12 03-Tomcat两大组件.assets/image-20201112211136090.png)

中间进行了数据类型的转换与封装。

![image-20201112211147272](/Users/jinpengpeng/Git/personal/www.oliver.ren/source/_posts/2020-11-12 03-Tomcat两大组件.assets/image-20201112211147272.png)

addConnector与setContainer的原因是Container只能有一个,而Connector可以有多个。

Tomcat总体架构

image-20201112193536442

Server:首先是一个服务器;

Service:可以有多个;

Connector:连接器,负责对外沟通,提供服务入口;

Engine(Container):负责具体的请求的处理。

Acceptor:接收器;

Executor:执行线程池;

Hppt11Processor:HTTP协议的处理器;

Coyote Adaptor:适配器;

Pepeline:职责链,一个一个的过。

EngineValue:引擎是否匹配;

HostValue:域名是否匹配;

ContextValue:应用的路径是否匹配;

WarpperValue:Servlet是否匹配。

Tomcat的三种部署模式

Tomcat的部署模式有三种,两种显式部署和一种隐式部署。

1.显式部署:添加context元素方式(server.xml):

2.显式部署:创建xml的方式(位置:conf/Catalina/localhost/xxx-path-name.xml)

reloadable=当更新的时候是否自动加载

3.隐式部署:

将一个war文件或者整个应用程序复制到Tomcat的webapps

推荐使用第二种方式,只要备份单个的XML配置文件就好了。

Tomcat目录结构解析

bin

执行文件所在目录
.sh文件是linux上运行的,.bat文件是windows上运行的

lib

依赖的jar包和tomcat源码编译后的jar包

conf

catalina.policy

权限相关,可以查看源码中的Permission类 ,Tomcat是跑在jvm上的,所以有些默认的权限。

showdown:某些程序跑起来之后,需要关闭权限

![image-20201112122820592](2020-09-07 01-Tomcat目录结构解析.assets/image-20201112122820592.png)

server.xml:

Server->Service ->Executor(线程池)、Connector(连接器);

<Server port=端口,

这里的端口是关闭的时候需要用的端口。

<Service,要提供的服务的一种,一个Tomcat可以提供多种服务。

线程池,可以通过Jconsole工具来查看配置是否生效。

name=线程池的名字

namePrefix=线程名字的前缀

minSpareThreads=初始线程数

maxThreads=最大线程数,不设置的话一般是默认200

对外提供服务的连接器

port=对外提供服务的端口

protocol=协议,HTTP/1.1是http的连接器;AJP/1.3是AJP的连接器,与其他apache的组件做集群工作的时候使用

connectionTimeout=超时时间

maxThreads=最大线程数,如果配置了线程池则该值失效

acceptCount=最大连接数?

web.xml


default
org.apache.catalina.servlets.DefaultServlet

debug
0


listings
false

1

DefaultServlet默认的,加载静态文件 html,js,jpg等静态文件。


jsp
org.apache.jasper.servlet.JspServlet

fork
false


xpoweredBy
false

3

JspServlet专门处理jsp。


123
application/vnd.lotus-1-2-3


3dml
text/vnd.in3d.3dml

mime-mapping文件类型,其实就是Tomcat处理的文件类型。


index.html
index.htm
index.jsp

直接访问Tomcat的根路径的时候,首先会去找下面的这些文件。

log

日志相关的目录

  • catalina.xxxx.log

    tomcat的启动信息,JVM如何加载的;

    如果tomcat启动有问题了,可以来看这个日志定位问题。

  • localhost-xxx.log

    tomcat中具体的业务请求的日志信息

webapps

tomcat自带的部署应用的目录,不建议将应用程序放到这个里面,因为万一tomcat误删了,应用程序也就跟着一起被删了。

DDD-第六课

第六课-领域模型和软件资产的持续演进与沉淀

image-20200816194959598

本讲大纲:

1.从资产视角看软件开发;

2.演进式设计是软件开发活动的本质;

3.遗留资产的共存与改进;

4.重构支持设计演进,沉淀领域资产。

开发活动的业务价值和资产价值

image-20200816200232748

为什么强者恒强?

image-20200816200751251

1.资产复用;

2.质量提升;

3.迅捷响应;

4.数据洞察。

资产分类:

1.技术资产;

2.通用领域资产;

3.业务领域资产。

image-20200816201431611

领域资产是最合适的资产粒度。

领域资产的汉堡包隐喻:

image-20200816201934936

关键实践一:划分通用子域,提炼领域资产

image-20200816202041341

关键实践二:用契约描述资产能力和约束

image-20200816202143170

权利和义务

关键实践三:保持领域资产的持续演进

image-20200816202520592

演进式设计是软件开发活动的本质

image-20200816202912946

演进式设计:勇气和谦卑

一、敢于在信息不全时做出决策

1.问题域的渐进认知;

2.能力的持续提成;

3.避免过度设计。

image-20200816203236165

二、相信未来一定会变化,为变化做好充分准备

1.业务应用层的自动化测试

2.领域层的自动化测试

a.提供者测试;

b.消费者测试。

image-20200816203416607

三、始终假定自己可能不正确,并使用各种机会检验

image-20200816203754985

image-20200816203824245

统一语言!!!

遗留资产的共存与改进

遗留资产

image-20200816204117544

1.遗留资产充斥技术债务;

2.接口不清晰,边界模糊,副作用未知;

3.代码理解困难,修改可能导致不可预知的副作用;

4.往往缺乏自动化测试。

对待遗留资产的三种态度:

image-20200816211446630

对待遗留资产的第四种态度:

image-20200816211624330

四种基本实践:

1.使用防腐层;

2.在遗留资产上包装并暴露清晰的API;

3.找到遗留资产的接缝,进行扩展;

4.细分遗留资产的边界,进行逐步替换。

使用防腐层:

image-20200816212105436

在遗留资产上包装并暴露API:

image-20200816212316328

找到遗留资产的接缝,进行扩展:

image-20200816212516137

细分遗留资产的边界,进行逐步替换:

image-20200816212615496

image-20200816212628865

1.从价值出发;

2.找到尽可能小的边界;

3.使用自动化测试保护;

4.按领域模型定义接口;

5.编写适配代码;

6.编写新代码,完成替换。

重构支持设计演进,沉淀领域资产

image-20200816213028953

image-20200816213043851

image-20200816213134132

关注点分离是重构的核心要点:

image-20200816213204137

image-20200816213251997

image-20200816214628881

DDD-第五课

第五课-微服务规划:以领域为中心规划服务

image-20200808211545634

架构反应了重要的决策:

架构定义了系统在其环境中的基本概念或特性,体现在其元素、关系以及其设计和演化的原则。

Fundamental concepts or properties of a system in its environment embodied in its elements, relationships, and in the principles of its dedign and evolution.

——ISO/IEC 42010:Systems and software engineering

——Architecture description

技术演进降低决策难度,聚焦于解决真正的问题

image-20200808213249079

本讲大纲:

1.子域和限界上下文,从问题域出发分而治之;

2.微服务带来了真正的边界;

3.上下文映射、API接口和设计契约。

image-20200808213856766

软件的复杂性和关注点分离

image-20200808214249268

分而治之在解决复杂问题的同时带来更大价值:

1.各个击破;

2.独立进化;

3.易于复用。

image-20200808214532679

以领域为核心——领域和子域

避免过大的领域模型。用一套模型来描述整个软件系统并不现实

1.简化认知——人并不能同时记住太多概念,刻意地忽略;

2.隔离变化——分而治之,每次处理一个特定方面,隐藏无关信息;

3.聚焦重点——区分那些对业务最重要的因素和次要的因素。

DDD中的相关模式

1.核心域:简化模型。定义一个核心域,并将其与大量支持模型和代码区分开来。

提炼核心域中最有价值和最专业的概念,把核心变小;

将顶尖人才分配到核心域中;

把精力花在核心域上,发现深层模型,确保获得足以实现系统愿景的灵活设计。

2.通用子域:识别那些非核心的领域。

分离通用子域,并把它们放在单独的模块中;

不要让通用子域包含核心域的知识;

考虑分离通用子域的开发人员,最好是使用既有的解决方案。

3.领域愿景陈述:明确核心领域的价值主张。

篇幅不要超过一页;

价值主张需要明确——只提现本领域模型和其他领域具有显著差异化的方面;

尽早完成,并在产生新的洞解时及时解决。

image-20200808215558794

如何拆分子域?

最重要的原则:领域体现的是业务能力

1.能不能用一句话说清目标?

2.是不是有一组内聚的核心概念?

3.有没有可能成为一项独立的业务?

启发式原则:

1.观察业务流程;

2.观察领域模型;

3.观察分析过程。

在业务流程中识别领域

image-20200808215858392

image-20200808220033360

image-20200808220053381

image-20200808220110863

image-20200808220127259

在领域模型中识别领域

image-20200808220222829

image-20200808220336158

演进纬度

image-20200808220654942

领域和子域拆分的要点总结:

1.分而治之是复杂性管理的基本方法,而且有助于隔离变化,提升复用;

2.领域的划分应该从业务视角出发,而不是实现视角;

3.在业务流程分析和领域模型中自然地分解,并保持持续演化的心智;

4.区分核心域和通用域,并在核心域上进行重点投资。

限界上下文(Bounded Context):最好的工作边界和问题域一致的边界。

限界上线问是什么:限界上下文是一个显式的边界。

image-20200808221338866

限界上下文和子域

观点:如果你在构建全新的系统,请让限界上下文的边界和子域定义保持一致。

image-20200808221547214

用领域模型和统一语言抵御破坏边界的冲动:

障碍1:环境——认知不足和习惯思维;

障碍2:外部——客户压力;

障碍3:自身——缺乏抽象习惯,不注意关注点分离;

不正确的实现破坏了领域边界:

image-20200808222034948

point:评论获得的积分;

isAdminMessage:是否管理员的回复;

memberService:与point是一样的问题。

微服务带来了真正的边界

微服务是一种架构风格

image-20200808222400345

微服务和 子域/限界上下文 紧密相关

image-20200808222445557

以服务的方式实现组件化

image-20200808222551562

image-20200808222718357

围绕业务能力进行组织:

image-20200808222841695

子域和服务规划:以领域作为核心关注点,兼顾非功能性需求,保持持续演化

image-20200808223122703

服务是一种组件化方式

image-20200808223534541

微服务和限界上下文彼此呼应:

1.领域需要一个边界(限界上下文);

2.微服务带来了真正的边界;

3.领域需要具有明显的业务价值,并保持持续生长;

4.微服务倡导围绕着业务价值构建服务和团队;

5.在领域层使用领域模型指导服务划分。并不需要为每个构造块定义一个服务,但是构造块提供了服务拆分的良好参考。

上下文映射、API接口和设计契约

上下文映射

image-20200808224325524

共享带来耦合,转化带来损失。

上下文映射的模式:

image-20200808224413604

image-20200808224503653

image-20200808224741892

观点:

如果提供方接口变化带来了问题,但是消费方未能发现,是消费方的责任。

image-20200808225401468

客户供应商:

1.依赖方(可以有多个)是被依赖的客户;

2.依赖方和被依赖方共同开发自动化验收测试,并且加入到被依赖的测试套件中;

3.非常适用于被依赖方的领域模型需要探索的情况。

防腐层ACL

被依赖方对依赖方没有承诺;

被依赖方的领域模型质量不好,或者不符合依赖方的需求,或者依赖方不想和被依赖方捆绑在一起

image-20200808225720110

事件机制和上下文映射

事件是解耦业务领域的非常关键的方法

image-20200808225825200

image-20200808225907889

image-20200808230111909

DDD-第四课

第四课-高效高质量编码:测试先行和意图导向的编程

image-20200808194825189

本讲大纲:

1.由外而内——发现和形成设计职责;

2.测试先行——表达和实现设计契约;

3.持续演进——契约是设计质量的保证。

从哪里写下第一行代码?

image-20200808195110856

自底向上的编程是很多浪费和麻烦的开始。

一个小游戏背后的故事:

image-20200808195400656

image-20200808195432178

image-20200808195601010

image-20200808195642014

设计是一种信息不完全情况下的决策。

由外而内的编程:

1.一般来说,外层的功能比底层的功能具有更高的确定性;

2.延迟决策到最后时刻,关键信息经常会自然显现;

3.由外而内的编程允许暂时忽略不重要的细节;

4.由外而内的编程——》“意图导向编程”。

意图导向编程(Programming By Intention)

意图导向编程就是“想什么就写什么”,用代码来表述你期望的行为。

想象:编码过程中,假设已经有一个理想的方法支持你的工作,你会怎么编写程序?那就这样编写它。

image-20200808200328949

CollisionDetector的具体实现

image-20200808201222838

底层设计一半来说涉及更多细节;由外而内的方式,让底层可以随时被替换。

由外而内、意图导向编程的收益:

1.职责分配是在编码过程中即时完成的,设计和编码真正成为一件事情;

2.代码的可理解性可以得到保证(声明式编程),消除了大多数注释的必要性;

3.以终为始,逐渐导出设计契约;

4.延迟复杂的细节性决策,聚焦于当前阶段的关键行为;

5.利用了IDE的“自动纠错”能力,提升了编码速度。

由外而内编程的2个要素:

1.保持同一个层次的抽象;

2.合理分配职责。

image-20200808201859982

对象的职责在编码过程中自然产生:

image-20200808202050709

所以第一行代码应该从上往下,从功能开始,是最合理的方式,即先写应用层。

创建属性计划的Service:

image-20200808202336406

应用场景驱动,促进领域对象和领域服务的职责持续生长

image-20200808202541403

由外而内,意图导向的编程要点小结:

1.从应用场景出发开始编码;

2.意图导向,描述期望的程序行为,同时完成职责分配;

3.逐层深入,重复上述步骤;

4.涉及领域层对象时,要结合领域对象的职责考虑,并合理区分哪些属于领域层,哪些不属于领域层。

测试先行

测试先行=意图导向编程+用测试表达设计契约

后置的自动化单元测试很难取得成功,原因:

1.上下文丢失;

2.可测性缺乏;

3.情绪和动机。

推荐实践:自动化测试代码先于产品代码完成

image-20200808203758561

开始编写测试——表达意图

image-20200808203858637

契约和手册:TripPlan服务,接收TripPlanCreationCmd,并且返回TripPlanDTO对象,初始状态为CREATED。

开始编写测试——修复错误,完善契约

image-20200808204143708

编写实现代码,实现契约:

image-20200808204318378

image-20200808204343730

增加持久化关注点:

image-20200808204429541

增加REST接口,并在集成环境中测试

image-20200808204456514

image-20200808204546745

image-20200808204559730

覆盖率是一种指示器,也是测试先行的自然结果:

image-20200808204650194

单元测试不是“白盒”测试:

image-20200808204808227

image-20200808205042035

小结:测试先行的收益

1.聚焦于外部行为;

2.自动化测试形成了代码的功能手册;

3.可测性得到了保证;

4.好的可测试性带来了模块化;

5.测试先行容易推动进展;

6.持续建立信心。

持续演进

软件开发中的契约:

image-20200808205639934

legacy code==code without tests

遗留代码,就是没有自动化测试的代码。

自动化测试=自动检查的设计契约。

总结:高效,高质量编码

image-20200808210003956

由外而内,意图导向;

测试先行,定义契约;

渐进认知,持续重构。

软件开发一定要关注设计能力。

image-20200808210241246