命令模式
命令模式将请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,然后该对象在执行命令。属于行为模式。
优点
- 降低了系统的耦合度。
- 新的命令可以很容易添加到系统中去。
确定
- 使用命令模式可能会导致某些系统有过多的具体命令类。
适用场景
- 认为是命令的地方都可以使用命令模式。比如:GUI中每一个按钮都是一条命令。
- 模拟CMD。
类图

责任链模式就是为请求创建一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。属于行为型模式。
每个接收者都包含下一个接收者的引用,如果该接收者不能处理该请求,那么就把该请求传递到下一个接收者。

声明一个抽象类Logger,在Logger中声明一个抽象方法write。然后声明4个继承Logger的类,并实现write方法。
1 | DebugLogger debugLogger = new DebugLogger(1); |
在一个抽象类中公开定义执行它的方法的模板。它的子类可以按需重写方法,但调用将以抽象类中定义的方式进行。

1 | Game dota = new Dota(); |
1 | new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,milliseconds,runnableTaskQueue,handler); |
zookeeper是一个开源的分布式服务。分布式应用可以基于zk实现的功能包括:数据发布/订阅、负载均衡、命名服务、集群管理、master选举、分布式锁、分布式队列等。
在zookeeper中,主要有三种角色:
session指的是客户端会话。在zk中,一个客户端连接指的是客户端和zk服务器之间的长连接。zk对外的默认端口是2181,客户端启动时,首先会与服务器建立一个TCP长连接,从第一次连接建立开始,客户端会话的生命周期也就开始了,通过这个长连接,客户端能够通过心跳检测和服务器端保持有效的会话,能够向zk服务器发送请求并接受响应,同时还能接收来自服务器的watch事件通知。SessionTimeout用来设置一个客户端会话的超时时间。当服务器压力太大、网络故障或者是客户端主动断开连接等各种原因,只要客户端在SessionTimeout规定的时间内能够重新连接上集群中任意一台服务器,那之前创建的会话仍然有效。
ZNode是zookeeper数据模型中的数据单元,zk将所有数据都存储在内存中,数据模型是一个树形结构(ZNode Tree),有斜杠(/)进行路径的分割,就是一个ZNode,例如:/root/children,其中root和children都是ZNode,root是children的父级。每个ZNode上都会保存自己的数据内容。
ZNode可以分为持久节点、临时节点、持久顺序节点和临时顺序节点。
1 | cZxid = 0x1313f3d4d9 |
在zk中,能改变zk服务器状态的操作被称为事务操作。一般包括数据节点的创建、删除、数据内容更新和客户端会话创建与失效等操作。对每一个事务请求,zk都会为其分配一个全局唯一的事务id,用zxid表示,通常是一个64位的数字。每个zxid对应一次更新操作,从zxid中可以间接识别出zk处理这些事务操作请求的全局顺序。
watcher是zk中一个很重要的特性。zk允许用户在指定节点上注册一些watcher,并且在一些特定事件触发的时候,zk服务端会将事件通知到感兴趣的客户端上去。该机制是zk实现分布式协调服务的重要特性。
zk采用ACL(Access Control Lists)策略来进行权限控制。zk定义了5中权限:
注意:CREATE和DELETE都是针对子节点的权限控制。
zk是一个高可用的分布式数据管理与协调框架。基于ZAB算法的实现,该框架能够很好的保证分布式环境中数据的一致性。
数据发布/订阅就是通常所说的配置中心,发布者将数据发布到zk节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和动态更新。
在系统开发中,经常会有这样的需求:系统需要使用一些通用的配置信息,例如服务器列表信息、数据库配置信息等。这些全局配置信息通常有以下三个特性。
对于这样的全局配置信息就可以发布到zk上,上客户端去订阅该消息。发布\订阅系统一般有两种设计模式,分别是推(push)和拉(pull)模式。
推:服务器端主动将数据更新发给所有订阅的客户端。拉:客户端主动发起请求来获取最新数据,通常客户端采用定时轮询拉取的方式。
zk采用的是推拉结合的方式。如下:客户端想服务端注册自己需要关注的节点,一旦该节点的数据发生变更,那么服务器端就会向相应的客户端发送watcher事件通知,客户端收到这个消息后,需要主动到服务器端获取最新的数据(推拉结合)。
命名服务是分布式系统中比较常见的一种场景。在分布式系统中,使用命名服务,客户端应用能够根据指定名字来获取资源或者服务的地址、提供者等信息。被命名的实体通常可以是集群中的机器、提供的服务、远程对象等等。较为常见的就是一些分布式服务框架中的服务地址列表。通过在zk里创建顺序节点,能够很容易创建一个全局唯一的路径,这个路径就可以作为一个名字。
master选举是zk最典型的应用场景。利用zk的强一致性,能够很好的保证在分布式高并发情况下节点的创建一定能够保证全局唯一性,即zk将保证客户端无法创建一个已经存在的znode。如果多个客户端请求创建一个临时节点,那么最终一定只有一个客户端请求能够创建成功。利用这个特性,能够在分布式环境中进行master选举了。
成功创建该节点的客户端所在的机器就成为了Master。同时,其他没有成功创建该节点的客户端,都会在该节点上注册一个子节点变更的Watcher,用于监控当前Master机器是否存活,一旦发现当前的Master挂了,那么其他客户端将会重新进行Master选举。这样就实现了Master的动态选举。
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。分布式锁可以分为排他锁和共享锁两种。
总结:++可以多个事务同时获得一个对象的共享锁,有共享锁就不能再加排他锁。++
zookeeper提供了单机、分布式和伪分布式三种模式。
1 | #zookeeper的默认配置文件zoo.cfg: |
条件有限,以伪分布式为例,将zookeeper文件夹复制三份,基本配置如下:
1 | #第一个 |
1 | #第二个 |
1 | #第三个 |
伪分布式和分布式配置的区别在于,伪分布式配置IP地址相同,端口号不同。
其中,localhost:7889:8890,第一个端口号用来通信,第二个端口号用来进行leader选举。
windows系统使用%zk_home%/bin目录下的zkServer.cmd启动zookeeper。由于在伪分布式下部署了三个节点,所以依次进入每个文件夹中,启动zookeeper。启动的时候可能会报错。
1 | 2017-02-09 14:21:38,871 [myid:] - ERROR [main:QuorumPeerMain@85] - Invalid confi |
错误原因是没有找到myid这个文件(如11、12行),需要在配置文件dataDir指定的目录下面(我的是:D:\zookeeper\zookeeper-3.4.9-1\data)建立一个myid文件,内容为1。依次在剩下的两个文件夹中也建立一个myid的文件,内容分别为2和3。
保存后,再次启动,发现又出现异常了,异常比较多,仅截取部分:
1 | 2017-02-09 14:32:32,467 [myid:1] - WARN [QuorumPeer[myid=1]/0:0:0:0:0:0:0:0:218 |
这个异常其实可以忽略,仔细看2、3、31、32行打印出的信息就明白了。出现这个异常的原因是因为,现在只启动了一个zk节点,而在配置文件中却写了三个zk的地址,第一个zk节点还无法与剩下的两个节点进行通信,所以只要把三个zk节点都启动就没问题了。

为了防止失效的链接请求报文突然又传到了服务端,因而产生错误。例如:“已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文并没有丢失,而是在某个网络节点长时间滞留,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。当server却以为新的运输连接已经建立,并一直等待client发来数据。这样,sever的很多资源就白白浪费了。采用三次握手可以防止上述现象的发生。例如刚才那种情况,client不会向server发出确认。server由于收不到确认,就知道client并没有要求建立连接。
通过三次握手可以确认客户端和服务器端的收发功能是否正常。client确认了:自己发送、接收正常,对方发送、接收正常;server确认了:自己发送、接收正常,对方发送、接收正常。
client不断向sever发送请求,server会给每个请求创建一个连接,然后向client发送创建链接时的回复,然后进行等待客户端发送第三次握手数据包。DDos攻击就是这样的原理,第一个D表示的是分布式distribute。让server产生大量的等待client回复的连接,浪费系统资源,并且会使正常的请求进不来。
简单说服务器发送连接请求,首先:

由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。
TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP是全双工模式,这就意味着,当主机1发出FIN报文段时,只是表示主机1已经没有数据要发送了,主机1告诉主机2,它的数据已经全部发送完毕了;但是,这个时候主机1还是可以接受来自主机2的数据;当主机2返回ACK报文段时,表示它已经知道主机1没有数据发送了,但是主机2还是可以发送数据到主机1的;当主机2也发送了FIN报文段时,这个时候就表示主机2也没有数据要发送了,就会告诉主机1,我也没有数据要发送了,之后彼此就会愉快的中断这次TCP连接。
这是因为服务器端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,收到对方的FIN报文时,仅仅表示对方不在发送数据了但是还能接受数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。
http长连接和短连接本质上是TCP长连接和短连接。http属于应用层协议,在传输层使用TCP协议,在网络层使用IP协议,并且顺序与发送顺序一致。TCP协议是可靠的、面向连接的。
无状态指的是对于事务没有处理记忆能力,服务器不知道客户端是什么状态。也就是说,在同一个web站点10分钟前打开一个页面和现在打开一个页面,它们之间是没有任何联系的。HTTP是一个无状态的面向连接的协议,无状态不代表HTTP不能保持TCP连接,更不能代表HTTP使用的UDP协议。
在HTTP/1.0中默认使用的是短连接。也就是说,客户端每次和服务器进行http操作,就会建立一次连接,任务结束连接也就中断。比如,访问某个页面,页面中包含图片、css、js等多个资源,这样的话,会建立多次连接。
而在HTTP/1.1中,默认使用了长连接,保持连接特性。使用长连接的HTTP协议,会在响应头加入这行代码 Connection:keep-alive。
在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用户传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这条已经建立的连接。keep-alive不会永久保持连接,他有一个保持时间,可以在不同的服务器软件中设定。实现长连接需要客户端和服务器端都支持长连接。
长连接可以省去较多的TCP建立和关闭操作,减少浪费,节约时间。对于频繁请求资源的客户端适合使用长连接。在长连接的应用场景下,client端一般不会主动关闭连接,当client与server之间的连接一直不关闭,随着客户端连接越来越多,server会保持过多连接。这时候server端需要采取一些策略,如关闭一些长时间没有请求发生的连接,这样可以避免一些恶意连接导致server端服务受损;如果条件允许则可以限制每个客户端的最大长连接数,这样可以完全避免恶意的客户端拖垮整体后端服务。
短连接对于服务器来说管理较为简单,存在的连接都是有用的连接,不需要额外的控制手段。但如果客户请求频繁,将在TCP的建立和关闭操作上浪费较多时间和带宽。
短连接的操作步骤是:
建立连接——数据传输——关闭连接…建立连接——数据传输——关闭连接
长连接的操作步骤是:
建立连接——数据传输…(保持连接)…数据传输——关闭连接

节点 | 角色说明
—|—
provider | 暴露服务的服务提供方
consumer | 调用远程服务的服务消费方
registry | 服务注册于发现的注册中心
Monitor | 统计服务的调用次数和调用时间的监控中心
Container| 服务运行容器
dubbo架构具有以下几个特点:分别是连通性、健壮性、伸缩性以及未来架构的升级性。
当服务集群规模进一步扩大,带动IT治理结构进一步升级,需要实现动态部署,进行流动计算,现有分布式服务架构不会带来阻力。下图是未来可能的一种架构:
| 节点 | 说明 |
|---|---|
| Deployer | 自动部署服务的本地代理 |
| Repository | 仓库用于存储服务应用发布包 |
| Scheduler | 调度中心基于服务压力自动增减服务提供者 |
| Admin | 统一管理控制台 |
| Registry | 服务注册与发现的注册中心 |
| Monitor | 统计服务的调用次数和调用压力的监控中心 |
1.本地配置很简单,和使用本地的service类,没有区别。
1 | <bean id=“xxxService” class=“com.xxx.XxxServiceImpl” /> |
2.远程配置需要将本地的配置拆分成两份,将服务定义部分放在服务提供方remote-provider.xml,将服务引用部分放在服务消费方remote-consumer.xml;并在提供方增加暴露服务配置
remote-provider.xml:
1 | <!-- 和本地服务一样实现远程服务;加上url="ip:port",相当于直连,不经过注册中心,一般本地调试的时候,会设置成localhost --> |
remote-consumer.xml:
1 | <!-- 增加引用远程服务配置 --> |
| 标签 | 用途 | 解释 |
|---|---|---|
| 服务配置 | 用于暴露一个服务,定义服务的元信息,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心 | |
| 引用配置 | 用于创建一个远程服务代理,一个引用可以指向多个注册中心 | |
| 协议配置 | 用于配置提供服务的协议信息,协议由提供方指定,消费方被动接受 | |
| 应用配置 | 用于配置当前的应用信息,不管该应用是提供者还是消费者 | |
| 模块配置 | 用于配置当前模块信息,可选 | |
| 注册中心配置 | 用于配置连接注册中心相关信息 | |
| 监控中心配置 | 用于配置连接监控中心相关信息,可选 | |
| 提供方配置 | 当ProtocolConfig和ServerConfig某属性没有配置时,采用此缺省值,可选 | |
| 消费方配置 | 当ReferenceConfig某属性没有配置时,采用此缺省值,可选 | |
| 方法配置 | 用于ServiceConfig和ReferenceConfig指定方法级的配置信息 | |
| 参数配置 | 用于指定方法参数配置 |
配置覆盖关系
服务提供方的配置,通过URL经过注册中心传递给消费方。
下图以timeout参数为例,展示了参数配置的传递过程:
如果公共配置很简单,没有多注册中心,多协议等情况,或者想多个 Spring 容器想共享配置,可以使用 dubbo.properties 作为缺省配置。
Dubbo 将自动加载 classpath 根目录下的 dubbo.properties,可以通过JVM启动参数 -Ddubbo.properties.file=xxx.properties 改变缺省配置位置。
略
略
==实际开发中,使用xml配置居多,至少我们公司是这样。==