项目搭建
首先说一下 zk,zk的客户端和服务端版本一定要一致,不然真是血的教训。
一定要把dubbo相关配置提取到一个common工程中配置,不然也会发生因为代码提交,环境切换造成注册地址不一样。
开发过程
其实就是工程拆分,接口和pojo 一个工程,serviceImpl,dao 一个工程,web一个工程,这样的三个工程构成基础分布式架构,dubbo.xml 配置文件中一定要写好服务名称,不然就会有调用不到,你还以为是spring注入怎样了。
另外开发不规范,pojo不继承基础类,不能实现序列化接口,虽然不起眼但是足以让你们找一天,因为你在debug时它就根本不进方法,不报错。另;重写toString( )也是有必要的!!!javaBean的规范真是太重要了!
zk的注册一定要加以保护,很多次大家把自己本地服务都注册到了一个zk上,这样就是你的功能一会好,一会坏其蛋疼程度不言而喻。
1.服务版本号
- 引用只会找相应版本的服务
- <dubbo:serviceinterface=“com.xxx.XxxService” ref=“xxxService” version=“1.0” />
- <dubbo:referenceid=“xxxService” interface=“com.xxx.XxxService” version=“1.0”/>
- 为了今后更换接口定义发布在线时,可不停机发布,使用版本号
2.暴露一个内网一个外网IP问题
为了在测试环境提供一个内网访问的地址和一个办公区访问的地址。
•增加一个指定IP为内网地址的服务协议
•增加一个不指定IP的服务协议,但是在/etc/hosts中hostname对应的IP要为外网IP
上面这种方案是一开始使用的方案,后面发现dubbo在启动过程无论是否配路由还是会一个个去连接,虽然不影响启动,但是由于存在超时所以会影响启动时间,而且每台机器还得特别配置指定IP,后面使用另外一套方案:
- 服务不配置ip,绑定到0.0.0.0,自动获取保证获取到是内网IP注册到注册中心即可,如果不是想要的IP,可以在/etc/hosts中通过绑定Hostname指定IP
- 内网访问方式通过注册中心或者直连指定内网IP和端口
- 外网访问方式通过直连指定外网IP和端口
使用这种方式需要注意做好防火墙控制等,比如在线默认也是不指定IP,会绑定在0.0.0.0,如果非法人员知道调用的外网IP和端口,而且可以直接访问就麻烦了(如果在应用中做IP拦截也成,需要注意有防范措施)。
3.dubbo reference注解问题
前文介绍使用时已经提到过,@Reference只能在spring bean实例对应的当前类中使用,暂时无法在父类使用;如果确实要在父类声明一个引用,可通过配置文件配置dubbo:reference,然后在需要引用的地方跟引用spring bean一样就行
4.服务超时问题
目前如果存在超时,情况基本都在如下几点:
- 客户端耗时大,也就是超时异常时的client elapsed xxx,这个是从创建Future对象开始到使用channel发出请求的这段时间,中间没有复杂操作,只要CPU没问题基本不会出现大耗时,顶多1ms属于正常
- IOThread繁忙,默认情况下,dubbo协议一个客户端与一个服务提供者会建立一个共享长连接,如果某个客户端处于特别繁忙而且一直往一个服务提供者塞请求,可能造成IOThread阻塞,一般非常特殊的情况才会出现
- 服务端工作线程池中线程全部繁忙,接收消息后塞入队列等待,如果等待时间比预想长会引起超时
- 网络抖动,如果上述情况都排除了,还出现在请求发出后,服务接收请求前超过预想时间,只能归类到网络抖动了,需要SA一起查看问题
- 服务自身耗时大,这个需要应用自身做好耗时统计,当出现这种情况的时候需要用数据来说明问题及规划优化方案,建议采用缓存埋点的方式统计服务中各个执行阶段的耗时情况,最终如果超过预想时间则把缓存统计的耗时情况打日志,减少日志量,且能够得到更明确的信息
现在我们应用使用过程中发现两种类型的耗时,一种我们目前只能归类到网络抖动,后续需要找运维一起关注这个问题,另外一种是由于一些历史原因,数据库查询容易发生抖动,总有一个时间点会突然多出很多超时。
5.服务保护
服务保护的原则上是避免发生类似雪崩效应,尽量将异常控制在服务周围,不要扩散开。
说到雪崩效应,还得提下dubbo自身的重试机制,默认3次,当失败时会进行重试,这样在某个时间点出现性能问题,然后调用方再连续重复调用,很容易引起雪崩,建议的话还是很据业务情况规划好如何进行异常处理,何时进行重试。
服务保护的话,目前我们主要从以下几个方面来实施,也不成熟,还在摸索:
-
考虑服务的dubbo线程池类型(fix线程池的话考虑线程池大小)、数据库连接池、dubbo连接数限制是否都合适
-
考虑服务超时时间和重试的关系,设置合适的值
-
一定时间内服务异常数较大,则可考虑使用failfast让客户端请求直接返回或者让客户端不再请求
经领导推荐,还在学习Release it,后续有其他想法,再回头来编辑。
6.zkclient的问题
前文已经提到过zkclient有两个问题,修改服务器时间会导致容器挂掉;dubbo使用zkclient没有传超时时间导致zookeeper无法连接的时候,直接阻塞Integer.MAX_VALUE。
正在调研curator,目前只能说curator不会在无法连接的时候直接阻塞。
另外zkclient和curator的jar包应该都是jdk1.6编译的,所以系统还在jdk1.5以下的话无法使用。
7.注册中心的分组group和服务的不同实现group
这两个东西完全不同的概念,使用的时候不要弄混了。
registry上可以配置group,用于区分不同分组的注册中心,比如在同一个注册中心下,有一部分注册信息是要给开发环境用的,有一部分注册信息时要给测试环境用的,可以分别用不同的group区分开,目前对这个理解还不透彻,大致就是用于区分不同环境。
service和reference上也可以配置group,这个用于区分同一个接口的不同实现,只有在reference上指定与service相同的group才会被发现,还有前文提到的分组合并结果也是用的这个。
部署工程
这里不得不说打包了,dubbo项目使用maven打包,的方式很多。一般都使用Ali官方的main方法打包。注意打包读取的assembly.xml文件。这里要仔细编写配置文件。要不你打不成的。
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>linux</id>
<formats>
<format>tar.gz</format>
</formats>
<fileSets>
<fileSet>
<directory>${project.basedir}/src/bin</directory>
<outputDirectory>/bin</outputDirectory>
<includes>
<include>*.sh</include>
</includes>
<fileMode>0755</fileMode>
</fileSet>
<fileSet>
<directory>${project.basedir}/src/conf</directory>
<outputDirectory>/conf</outputDirectory>
</fileSet>
<fileSet>
<directory>${project.basedir}/src/logs</directory>
<outputDirectory>/logs</outputDirectory>
<includes>
<include>*.log</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.basedir}/src/data</directory>
<outputDirectory>/data</outputDirectory>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<useProjectArtifact>true</useProjectArtifact>
<outputDirectory>lib</outputDirectory>
<scope>runtime</scope>
</dependencySet>
</dependencySets>
</assembly>