分布式架构入门

1)当服务越来越多时,服务URL配置管理变得非常困难,F5硬件负载均衡器的单点压力也越来越大。

2) 当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。

3) 接着,服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器?

我们迫切需要解决的问题:需要有一个注册中心,动态的注册和发现服务,使服务的位置透明。

分布式的特点和概念

  1. 是一种远程接口的调用
  2. 集群:在不同的服务器上部署相同的服务模块,通过负载均衡设备进行调度。
  3. 分布式:在不同的服务器上部署不同的服务模块,通过RPC/RMI或者自有的内部机制进行调度。
  4. 分布式集群:将某一个服务进行集群化
  5. 有利于开发速度,降低成本
  6. 网络延迟等,调试程序不太方便,对开发人员的要求比较高,抽取服务模块的时候不知道抽取成哪些模块

分布式的结构

传统模式

服务太多,调用太多的时候,URL/IP地址可能要配置到程序中的properties文件中,如果对方的地址变更(宕机)都需要手工处理。

注册中心模式

将提供服务的一端(provider)地址都统一维护到一个注册中心中,调用它的一方消费者(consumer)在每次调用的时候到注册中心去获取,当提供者地址更改或者宕机的时候,这个更改对消费者来说是透明的,即消费者不需要知道提供者具体的地址,具体的地址由注册中心分配。

认识dubbo和zookeeper

Dubbo

1  RPC框架:远程调用框架,服务通信(RPC)和服务管理两部分必不可少

2  功能:远程调用、注册中心、宕机检测、恢复后自动识别、负载均衡(软负载均衡,特点:成本比较低,硬件负载均衡:F5服务器)、统计功能(dubbo-monitor),管控台(dubbo-admin),其中后2者不是必须的,主要给运维使用。

3  依赖JDK和spring

 

Zookeeper

  1. apache软件基金会维护,是hadoop的一个组件,用的非常广泛
  2. 它也有注册中心的基本特点,例如:远程调用、注册中心、宕机检测、恢复后自动识别、负载均衡。
  3. 运行的时候依赖JDK

 

dubbo+zookeeper模型

zookeeper的安装和启动

zookeeper的依赖环境

Jdk 1.7

zookeeper的安装过程

[root@iZ2ze8y50ep4lui61ttg4pZ local]# wget https://archive.apache.org/dist/zookeeper/zookeeper-3.4.10/zookeeper-3.4.10.tar.gz

[root@iZ2ze8y50ep4lui61ttg4pZ local]# tar -zxvf zookeeper-3.4.10.tar.gz

[root@iZ2ze8y50ep4lui61ttg4pZ conf]# cp zoo_sample.cfg zoo.cfg

[root@iZ2ze8y50ep4lui61ttg4pZ conf]# cd ..

[root@iZ2ze8y50ep4lui61ttg4pZ zookeeper-3.4.10]# cd bin

[root@iZ2ze8y50ep4lui61ttg4pZ bin]# ./zkServer.sh start

ZooKeeper JMX enabled by default

Using config: /usr/local/zookeeper-3.4.10/bin/../conf/zoo.cfg

Starting zookeeper ... STARTED

启动成功后可以看到如上内容

  1. 在任何一个目录下启动zkServer.sh

 

需要按照自己的实际目录配置/etc/profile环境变量,内容类似为:

 

如果远程连接(provider/consumer)需要打开2181端口。在防火墙添加相应的命令即可,如果使用的是ECS服务器,则在安全策略中打开该端口。

 

改造传统开发工程的思路和开发准备

1 什么是provider?provider都有哪组成部分?

通常拆分service层,把接口和接口的实现拆分成2个工程,POJO、DAO不需要改动。

2 什么是consumer?Consumer都有哪些组成部分?

consumer就是web层,即之前的controller层,即它的变化是多种多样的,例如OA可能要调用service用户身份认证,一个EB商城系统也可能调用service层的身份认证。

3 provider/consumer改造之后的变化?

 

服务器的使用

  1. zookeeper这台需要开放2181端口
  2. providerA/providerB需要开放20880端口
  3. consumer如果远程连接provider的话,provider主机还需要修改/etc/hosts文件

开发我的第一个分布式应用系统

  1. 创建父工程,并在父工程中依次创建pojo,dao,api,service,controller等工程,父工程聚合这些子工程
  2. 添加依赖

添加工程之间的依赖,详情参考上面的分析图

3 导入第三方依赖

  1. 在父工程中导入常用的第三方jar包,子工程自动依赖
  2. 注意:servlet.jar,servlet-jsp.jar等单独放到web工程中
  3. 配置service工程
  4. 配置pom.xml文件以便能够打出可以执行的jar包及其依赖,见service工程的pom.xml文件。
  5. 配置spring.xml文件

写程序

见各个程序和pom.xml/spring.xml等配置文件

配置crmweb工程的web.xml、spring.xml和springmvc.xml

web.xml中只需要配置spring和springmvc部分

spring.xml内容如下:

接口包名必须跟service中interface保持一一对应(一致),否则提示找不到provider,id不要求一致

发布我的第一个SOA分布式应用程序

  1. 服务器环境的准备

开启zk

[root@iZwz922gt8apce892igiynZ bin]# ./zkServer.sh start

上传provider程序

在provider服务器的/usr/local目录下简历crm目录,将工程service中的 lib和provider.jar上传到该目录

备注:程序可以上传到任何一个目录,不影响运行结果,但必须保证lib和provider.jar在同一级目录。

启动provider程序

[root@iZ2ze8y50ep4lui61ttg4pZ local]# cd crm

[root@iZ2ze8y50ep4lui61ttg4pZ crm]# java -jar provider.jar &

[root@iZ2ze8y50ep4lui61ttg4pZ crm]# log4j:WARN No appenders could be found for logger (com.alibaba.dubbo.common.logger.LoggerFactory).

log4j:WARN Please initialize the log4j system properly.

log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

[2018-03-31 15:08:46] Dubbo service server started!

 

 

启动consumer端

分别运行http://localhost:8080/crmweb/setname?name=zhangsanfeng

和http://localhost:8080/crmweb/getusers查看调用效果

[2018-03-31 15:08:46] Dubbo service server started!

客户端传过来的名称为:张三丰

客户端传过来的名称为:张三丰

客户端传过来的名称为:张三丰

客户端传过来的名称为:张三丰

客户端传过来的名称为:张三丰

客户端传过来的名称为:张三丰

  1. 必须保证provider开启20880端口
  2. provider端的日志打印在ECS控制台,consumer端的日志打印在eclipse控制台或者tomcat控制台。

 

基于dubbo的分布式系统的运行机理和启动日志分析

  1. Provider.jar包启动的原理和过程

在provider.jar包中跟目录下有一个MANIFEST.MF文件,该文件内容大致为:

Built-By: HP

Build-Jdk: 1.7.0_72

Main-Class: com.alibaba.dubbo.container.Main

Class-Path: . lib/crmdao-0.0.1-SNAPSHOT.jar lib/crmpojo-0.0.1-SNAPSHOT

 .jar lib/crmapi-0.0.1-SNAPSHOT.jar lib/dubbo-2.5.8.jar lib/spring-con

 text-4.3.10.RELEASE.jar lib/spring-beans-4.3.10.RELEASE.jar lib/sprin

 g-web-4.3.10.RELEASE.jar lib/javassist-3.20.0-GA.jar lib/netty-3.2.5.

 Final.jar lib/zkclient-0.10.jar lib/slf4j-api-1.6.1.jar lib/zookeeper

 -3.4.8.jar lib/slf4j-log4j12-1.6.1.jar

其中main-class指定了正序的入口,也就是交给dubbo的Main类去启动,这个类默认会读取/spring/spring.xml配置文件。当读到配置文件中 <dubbo>标签时会交给DubboNamespaceHandler去处理,由它来解析xml配置文件,并将解析的内容封装到一个对象中,发送给ZK.

dubbo的容器

使用Main类启动程序的话,可以实现“优雅关机”

  1. consumer端启动原理和过程

基本过程跟provider端差不多,唯一不同的是consmer一般由tomcat等web容器来启动,通过web.xml里面配置spring.xml来完成spring容器的初始化工作。可看到,两端工作的核心都是spring容器的初始化。

  1. consumer和provider和zookeeper的通信过程

通信过程图

  1. 启动过程的日志分析

启动zookeeper时:

正常日志

启动provider时

——provider端的日志如下:

[DUBBO] Export dubbo service cn.crm.api.UserApi to url dubbo://192.168.187.173:20880/cn.crm.api.UserApi?anyhost=true&application=dubboprovider&bind.ip=192.168.187.173&bind.port=20880&dubbo=2.5.8&generic=false&interface=cn.crm.api.UserApi&methods=setName,getUsers&pid=9516&revision=0.0.1-SNAPSHOT&side=provider×tamp=1522804354272, dubbo version: 2.5.8, current host: 127.0.0.1

[DUBBO] Notify urls for subscribe url provider://192.168.187.173:20880/cn.crm.api.UserApi?anyhost=true&application=dubboprovider&category=configurators&check=false&dubbo=2.5.8&generic=false&interface=cn.crm.api.UserApi&methods=setName,getUsers&pid=9516&revision=0.0.1-SNAPSHOT&side=provider×tamp=1522804354272

——zookeeper端的日志如下:

2018-04-03 14:01:01,346 [myid:] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:58885

2018-04-03 14:01:01,353 [myid:] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:ZooKeeperServer@942] - Client attempting to establish new session at /127.0.0.1:58885

2018-04-03 14:01:01,359 [myid:] - INFO  [SyncThread:0:ZooKeeperServer@687] - Established session 0x1628a1781e60000 with negotiated timeout 30000 for client /127.0.0.1:58885

启动consumer时:

——zookeeper端的日志如下:

[NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@192] - Accepted socket connection from /127.0.0.1:52429

[NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:ZooKeeperServer@942] - Client attempting to establish new session at /127.0.0.1:52429

[SyncThread:0:ZooKeeperServer@687] - Established session 0x1628e3fd60a0002 with negotiated timeout 30000 for client /127.0.0.1:52429

 

——provider端日志如下:

无打印

  1. 关闭provider时:

——comsumer端日志如下:

com.alibaba.dubbo.remoting.RemotingException: client(url: dubbo://192.168.187.173:20880/cn.crm.api.UserApi?anyhost=true&application=dubboconsumer&check=false&codec=dubbo&dubbo=2.5.8&generic=false&heartbeat=60000&interface=cn.crm.api.UserApi&methods=setName,getUsers&pid=188®ister.ip=192.168.187.173&remote.timestamp=1522804991189&revision=0.0.1-SNAPSHOT&side=consumer×tamp=1522805139189) failed to connect to server 192.168.187.173:20880, error message is:Connection refused: no further information

at com.alibaba.dubbo.remoting.transport.netty.NettyClient.doConnect(NettyClient.java:124)

——zookeeper端日志如下:

WARN  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@373] - Exception causing close of session 0x1628e3fd60a0000 due to java.io.IOException: 远程主机强迫关闭了一个现有的连接。

重启provider时:

——comsumer端日志如下:

[ZkClient-EventThread-18-127.0.0.1:2181] INFO  com.alibaba.dubbo.remoting.transport.AbstractClient  -  [DUBBO] Successed connect to server /192.168.187.173:20880 from NettyClient 192.168.187.173 using dubbo version 2.5.8, channel is NettyChannel [channel=[id: 0x4d912f56, /192.168.187.173:52775 => /192.168.187.173:20880]], dubbo version: 2.5.8, current host: 192.168.187.173

——zookeeper端日志如下:

[SyncThread:0:ZooKeeperServer@687] - Established session 0x1628e46335e0001 with negotiated timeout 30000 for client /127.0.0.1:52774

[ProcessThread(sid:0 cport:2181)::PrepRequestProcessor@648] - Got user-level KeeperException when processing sessionid:0x1628e46335e0001 type:create cxid:0x4 zxid:0xd8 txntype:-1 reqpath:n/a Error Path:/dubbo/cn.crm.api.UserApi/configurators Error:KeeperErrorCode = NodeExists for /dubbo/cn.crm.api.UserApi/configurators

同理,重启comsumer 也有类似上述的日志。

关闭zookeeper时

——comsumer和provider端日志如下:

java.io.IOException: 远程主机强迫关闭了一个现有的连接。

at sun.nio.ch.SocketDispatcher.read0(Native Method)

at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43)

at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)

java.net.ConnectException: Connection refused: no further information

at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)

at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:739)

at org.apache.zookeeper.ClientCnxnSocketNIO.doTransport(ClientCnxnSocketNIO.java:361)

at org.apache.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:1141)

上述日志一直处于打印状态。

 

  1. 总结
  2. provider,consumer,zookeeper三者之间是可以相互感知的,即对方的启动、关闭另一方都会获取到他的状态信息(打印日志可以看出),因此可以认为“互联互通”,但是provider并不是跟consumer直连的,而是provider状态的改变通过ZK推送给的consumer。他们之间的连接是一个长连接。
  3. provider,consumer,zookeeper三者之间又是相互独立的,服务可以自动发现。
  4. 如果注册中心宕机了,重新启动之后,provider/consumer会去重新注册的。
  5. provider.jar重复启动就会报端口占用的错误,可以修改dubbo端口后再执行。
  6. 提供者provider,是一个无状态的java应用,无状态,即不需要进行类似web中session信息的保留,无状态的应用特别适合宕机切换。
  7. 消费者consumer,通常是一个有状态的web应用。可见,在provider这端不要进行有关session,request的处理——因为必须得有web容器(例如tomcat等)的支持。如果必须要传递session中的内容的话,可以封装成为一个实现了序列化接口的类对象,传递给provider进行处理。
  8. 从provider/consumer日志可看出,它们第一次连接zookeeper的过程基本相同。
  9. 第一次请求consumer和再次请求consumer过程对比:第一次运行consumer程序的时候,创建了consumer到zk和dubbo的连接,consumer得到provider返回结果的时间是10649毫秒。第二次运行consumer程序的时候,不再创建consumer到zk和dubbo的连接,consumer得到provider返回结果的时间是875毫秒。可见以后再请求的时候就不会再创建,并且是通过netty创建的一个长连接,这也是为什么第一次请求consumer比较耗时的原因。另外,第一次请求和第二次请求,tomcat后台打印的日志不一样,从日志也可以看出请求的过程。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值