前面我们讲了node.js来做网关,把前端的请求进行转发。但实际上,请求一个统一的地址,然后由服务器进行转发这种模式是有很大的缺陷的,前端因为其特殊性,只能这么做,但是如果在后端,我们也这样干,实际上我们就相当于做了个ESB。当请求量过大时,ESB服务器(提供服务发现和请求转发的服务器)就成为了瓶颈,用于转发服务的服务器,总有一天会爆掉,一旦爆掉,那就是全站事故,这是一件很恐怖的事情。事实上,我们公司最早的服务框架也是ESB的,请求量大了会出现各种各样的问题,所以现在新的服务框架也已经转为点对点的模式了。
我们总结一下java的soa client需要做的几件事情:
1、提供一个client,client运行在调用端。
2、client里能拿到zookeeper维护的服务地址,并且缓存起来。
3、当服务地址变化的时候(有服务挂掉了),client要更新自己的服务地址缓存。
4、client能将请求post服务地址。并将返回的内容反序列化为对象(java对象或.net对象)。
那么,我们的思路就出来了:
1、client是一个单利的。
2、client初始化的时候,通过zookeeper客户端拿到服务地址,并缓存。
3、client需要通过zookeeper时刻监听服务地址的变化情况。
4、client将Request对象封装为http请求。
5、client通过HttpClient将请求发出去(我翻了我们公司的源码,也是用的HttpClient来发送请求)
6、client将返回内容转换为Response对象
环境准备
1、我用的是idea
2、HttpClient如果大家想详细了解下它的特性和使用方法,可以看下网上的博客。例如:http://blog.csdn.net/wangpeng047/article/details/19624529
3、zookeeper的java客户端
4、序列化反序列化的工具gson
实现服务端
定义Request和Response
请求类GreetRequestType.java
username属性,用来传需要打招呼的人的名字
返回类GreetResponseType.java
greetInfo属性,用来返回打招呼的内容
然后我们实现服务端,跟HelloService相似,我们在Application
里加上GreetService服务,不过method是Post。服务的功能根据GreetRequestType
里的username字段,拼打招呼的内容,并赋值给greetInfo。比如输入cqin,返回Hello cqin。
Application如何实现服务的注册,我们在《微服务系统架构系列2——服务注册》已经讲过了。
通过springboot,在端口8080和8082分别启动springboot。具体启动方式,同样见《微服务系统架构系列2——服务注册》,这里就不重复写了。
然后我们用postman手动post请求到8080和8082,可以看到如下访问:
同时,我们通过zkCli.cmd连接到zookeeper的客户端,可以看到/registry/GreetService下有两个节点
这个说明,我们的服务端实现完成。
实现ServiceClientBase
ServiceClientBase主要分两个功能, 一个是zookeeper相关的,一个是httpclient相关的。
Zookeeper相关的包括服务地址的发现和监听。Httpclient相关的,主要是发送请求到服务端,并将请求转换为Response。
服务地址的发现和监听,监听的代码比较粗暴,我们是直接把服务里所有的节点删了节点 , 然后再加。这个如果在生产上,可以自己改进下监听的事件。
请求发送
客户端代码
调用代码
我们用junit,每隔1秒发送一个请求,连续发送100次。
实验结果
首先通过8080和8082端口启动GreetService。
然后通过UnitTest,启动HelloServiceClientTest。
此时,会随机访问8080和8082这两个端口,并返回Hello cqin。
然后,我们停掉8082的sprintboot,模拟机器出问题的情况。
此时,再继续访问8082,client会报错。之所以会有这个报错,是因为从机器挂掉,到我们的zk客户端监听到变化事件是有一个很小的时间差的。所以,如果服务是异常挂掉,会有一小端时间的报错,我们没有办法。但如果是正常发布,我们有很多办法可以保证不会出现这报错,比如在zk里定义一个valid的属性,准备拉出的时候,先将valid设置为false,客户端发现valid为false,则不往集群发送请求,间隔5s后,再拉出集群。
然后,由于我们监听过NodeChildrenChanged事件,接收到这个事件了之后, 我们会进行地址的更新。所以,此时,客户端只会访问到8080这个端口,报错不再发生。
然后,我们重新启动8082端口的springboot,等客户端再次接受到NodeChildrenChanged
事件之后,客户端会再次进行地址更新,之后,就可以访问到8082端口。
总结
我们基本实现了后台代码的服务发现,并且提供了一个client。这个client,能够自动发现服务的变化,缓存服务的地址,并帮助客户端将Requet序列化成json之后,通过HttpClient,post到服务端。
至此,服务端只需要定义一个java的Request文件和一个Response文件,并实现服务逻辑。客户端只需要调用HelloServiceClient.getInstance().greetService(request);就可以拿到服务的返回。
网友评论