三个核心问题:
HTTP协议的工作方式与HTTP网络协议栈的结构
如何实现基于HTTP协议和TCP协议的的RPC调用,它们之间有何差别,分别适用何种场景
如何实现服务的动态注册和路由,以及软负载均衡的实现
===========================================================
基于TCP协议的RPC:
RPC(Remote Process Call远程过程调用)。RPC将原来的本地调用转变为调用远端服务器上的方法,给系统处理能力和吞吐量带来了近似于无限制提升的可能。成熟方案有:RMI,webservice等。
将对象转换为二进制流的过程,即为对象的序列化。
将二进制流恢复为对象的过程则为对象的反序列化。
常用的序列化方案:Google的Protolcal Buffers,Java内置的序列化方式,JSON,XML等。
基于TCP协议实现RPC:
服务接口:
public interface SayHelloService{ public String sayHello*(String sayHelloArg); }
服务的实现:
public class SayHelloServiceImpl implements SayHelloService { @override public String sayHello(String helloArg){ if (helloArg.equals("hello") { return "hello"; } else { return "bye"; } } }
服务消费者Consumer
//接口名 String interfacename = SayHelloService.class.getName(); //需要远程执行的方法 Method method = SayHelloService.class.getMethod("sayHello",java.lang.String.class); //需要传递到远端的参数 Object[] args = {"hello"}; Socket socket = new Socket("127.0.0.1",12342); //将方法名和参数传到远端 ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); output.writeUTF(interfacename);//接口名 output.writeUTF(method.getName());//方法名 output.writeObject(method.getParameterTypes()); output.writeObject(args); //从远端读取方法执行结果 ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); Object result = input.readObject(); 先取得接口名称,需要调用的方法和需要传递的参数,并通过学习socket将其发送到服务提供方,等待服务端返回响应结果。
服务提供者provider类的代码:
ServerSocket server = new ServerSocket("12342"); while(true) { Socket socket = server.accept(); //读取服务信息 ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); String interfacename = input.readUTF();//接口名 String methodName = input.readUTF();//调用方法 Class<?>[] parameterTypes = (Class<?>[])input.readObject();//参数类型 Object [] arguments = (Object) input.readObject();//参数对象 //执行调用 Class serviceinterfaces = Class.forName(interfacename);/得到接口的class Object service = services.get(interfacename);//取得服务实现的对象 Method method = serviceinterfaceclass.getMethod(methodName, parameterTypes);//获取要调用的方法 Object result = method.invoke(service, arguments); ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); output.writeObject(result); } 服务提供端事先将服务实例化好后放在services这个Map,通过while不断地接收新到来的请求,得到所需要的参数,包括接口名称,参数类型,方法名称,通过java反射取得接口中需要调用的方法,执行后将结果返回给服务的消费者.
基于HTTP协议的RPC:
HTTP协议属于应用层协议,构建在TCP和IP协议之上,处于TCP/IP体系架构中的顶端。
//通过Apache HttpClient发送HTTP请求 //url前端加上http协议头,标明为http请求 String url = "//组装请求 HttpClient httClient = new DefaultHttpClient(); HttpGet httpGet = new HttpGet(url); //接收响应 HttpResponse response = httpClient.execute(httpGet); HttpEntity entity = response.getEntity(); byte[] bytes = EntityUtils.toByteArray(entity); String result = new String(bytes,"utf8");
JSON和XML:
//将java对象person序列化为json格式:jackson-all-1.7.6.jar 工具包
Person p = new Person(); p.setAddress("gdcc); p.setAge(18); p.setBirth(new Date()); p.setName("xiejunbo"); //json对象序列化 String personJson = null; ObjectMapper mapper = new ObjectMapper(); StringWriter sw = new StringWriter(); JsonGenerator gen = new JsonFactory().createJsonGenerator(sw); mapper.writeValue(gen, p); //JSON反序列化 Person p = (Person) mapper.readValue(personJson, Person.class);
//Java对象序列化为XML:xstream-1.4.4.jar工具包
Person p = new Person(); p.setAddress("gdcc"); p.setAge(18); p.setBirth(new Date()); p.setName("xiejunbo"); //将person序列化为XML XStream xStream = new XStream(new DomDriver()); //设置person类的别名 xStream.alias("person", Person.class); String personXML = xStream.toXML(person); //将XML反序列化为java对象 Person person = (Person) xStream.fromXML(personXML);
RESTFUL和RPC
RPC风格的URL:
http://hostname/provider.do?service=com.http.sayhello&format=json×tamp=20160417&arg1=XX&arg2=xx
hostname表示服务提供方的主机名,service表示远程调用的服务接口名,format表示返回参数格式, timestamp表示客户端请求的时间戳,arg1,arg2表示服务所需参数。
RESTFUL风格的一个核心思想是通过HTTP请求对应的POST,GET,PUT,DELETE方法,来完成对应的CURD操作。
服务的路由和负载均衡
服务化的演变:公共的业务被拆分出来,形成可共用的服务,最大程度地保障了代码和逻辑的复用,避免重复建设,这种设计称为SOA(Service-Oriented Architecture)
SOA架构中,服务消费者通过服务名称,在众多服务中找到要调用的服务的地址列表,称为服务的路由。
当服务越来越多,规模越来越大时,对应的机器数量越来越多,单靠人工管理和维护服务及地址配置信息,已经越来越困难。并且,依赖单一的硬件负载均衡设备或者使用LVS,NGINX等软件方案进行路由和负载均衡调度,单点故障的问题也开始明显,一旦服务路由或者负载均衡服务器宕机,依赖它的所有服务均失效。
此时需要一个能够动态注册和获取服务信息的地方,来统一管理服务名称和其对应的服务器列表信息,称之为服务配置中心。如下图:
说明:服务提供者在启动时,将其提供的服务名称,服务器地址注册到服务配置中心,服务消费者通过服务配置中心来获得需要的调用的服务的机器的列表,通过相应的负载均衡算法,选取其中一台服务器进行调用。
基于ZooKeeper的持久和非持久节点,可以近乎实时地感知到后端服务器的状态(上线,下线,宕机)。
负载均衡算法
1.轮询法(Round Robin)
将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端每一台服务器,不关心服务器实际的连接数和当前的负载。
2.随机法(Random)
通过系统随机函数,根据后端服务嘎啦列表的大小值来随机选取其中一台进行访问。
3.源地址哈希算法(Hash)
获取客户端访问的IP地址,通过哈希函数计算得到一个数值。用该数值对服务器列表大小进行取模运算,得到结果是要访问的服务器的序号。采用哈希法进行负载均衡,同一IP地址的客户端,当后端服务器列表不变时,每次都会被映射到同一台后端服务器进行访问。可在服务消费者和服务提供者之间建立有状态的session会话.
4.加权轮询法(Weight Round Robin)
根据权重大小,将地址重复地添加到服务器地址列表中,权重越大,该服务器每轮所获得的请求数量越多。
5.加权随机法(Weight Random)
按权重随机选取服务器
6.最小连接数法(Least Connections)
根据后端服务器当前的连接情况,动态地选取其中当前积压连接数最少的一台服务器来处理当前请求,尽可能地提高后端服务器的利用率,将负载合理地分流到每一台机器。
ZooKeeper集群结构
zookeeper是可以集群复制的,集群间通过zap(zookeeper automatic broadcast)保持数据的一致性。该协议看起来像是Paxos协议的某种变形,该协议包括两个阶段:leader election阶段和atomic broadcast阶段。集群中将选举出一个leader其它机器则称为follower,所有的写操作都被传送给leader,并通过broadcast将所有的更新告诉follower。当leader崩溃或失去大多数follower时,需要重新选举出一个新leader,让所有的服务器都恢复到一个正确的状态。当leader被选举出来,且大多数服务器完成了和leader的状态同步后,leader election的过程就结束了,将进入Automic broadcast的过程。Automic broadcast同步leader和follower之间的信息,保证leader和follower之间具有相同的系统状态.
zookeeper集群结构图:
zookeeper运行环境配置过程:
进入/usr,使用wget从官网下载zookeeper
解压并删除下载的tar.gz包
修改系统环境变量,在profile文件的最后部分导出zookeeper的安装路径
创建zookeeper配置文件
执行vi zoo.cfg,修改配置文件内容
创建配置中的相应目录
启动zookeeper
使用zookeeper自带的客户端工具zkCli.sh查看zookeeper的节点建议情况
查看根目录下的节点
ZooKeeper API
注:由于都zookeeper的watcher是一次性的,每次在处理完状态变化事件后,需要重新注册watcher,这个特性使得在处理事件和重新注册watcher这段时间发生的节点变化将无法被感知。
ZooKeeper第三方客户端工具包zkClient:
zkClient解决了watcher的一次性注册的问题,将znode的事件重新定义为:子节点的变化,数据的变化,连接及状态的变化,三类。由zkClient统一将watcher的WatchedEvent转换到以上三种情况中去处理,watcher执行后重新读取数据的同时,再注册相同的watcher。zkClient在发生session expire 异常时会自动创建新的Zookeeper实例进行重连.
HTTP服务网关:
相同功能,相同数据,在不同平台下没必要重复开发,可以利用搭建SOA体系,达成公共部分逻辑的复用,避免重复造轮子,降低开发运维成本。
gateway接收外部各种APP的http请求,完成受益人权限和安全验证,当校验通过后,根据传入的服务名,到配置中心找到对应的服务名称节点,并加载对应服务提供者的地址列表,通过负载均衡算法,选取机器发起远程调用,将客户端参数传递到后端服务端。服务提供方根据所传入的参数,给出正确的响应,当gateway接收到响应后,再将响应输出给客户端APP。
如下图是基于网关的安全架构:
注:服务提供者不直接对外提供服务,对于外部的APP来说,一旦gateway失效,所有依赖它的APP将无法使用。由于所有的请求都经过gateway进行安全校验和请求转发,其流量是整个后端集群流量之和。
网关集群架构方案,一组对等组成的服务器组成的网关集群。如下图:
注:网关的前面有两台负载均衡设备,负责对网关集群进行负载均衡。负载均衡设备之间进行心跳检测,一旦其中一台宕机,另一台则变更自己的地址,接管宕机的这台设备的流量。平时两台机器均对外提供服务。