这可能是讲分布式系统最到位的一篇文章( 五 )


 
通过 IP 条件表达式配置黑白名单访问控制:consumerIP != 192.168.1.1 。
 
只暴露部分服务提供者,防止这个集群服务都被冲垮,导致其他服务也不可用 。
例如providerIP=192.168.3* 。读写分离:method=find*,list*,get*,query*=>providerIP=192.168.1. 。前后台分离:App=web=>providerIP=192.168.1.,app=java=>providerIP=192.168.2. 。灰度升级:将WEB前台应用理由到新的服务版本上:app=web=>provicerIP=192.168.1.* 。 
序列化与反序列化
 
把对象转换为字节序列的过程称为序列化,把字节序列恢复为对象的过程称为反序列化 。
 
运程调用的时候,我们需要先将 Java 对象进行序列化,然后通过网络,IO 进行传输,当到达目的地之后,再进行反序列化获取到我们想要的结果对象 。
 
分布式系统中,传输的对象会很多,这就要求序列化速度快,产生字节序列小的序列化技术 。
 
序列化技术:Serializable,XML,Jackson,MessagePack,FastJson,Protocol Buffer,Thrift,Gson,Avro,Hessian 等 。
 
Serializable 是 Java 自带的序列化技术,无法跨平台,序列化和反序列化的速度相对较慢 。
 
XML 技术多平台支持好,常用于与银行交互的报文,但是其字节序列产生较大,不太适合用作分布式通讯框架 。
 
FastJson 是 Java 语言编写的高性能的 JSON 处理器,由阿里巴巴公司开发,字节序列为 json 串,可读性好,序列化也速度非常的快 。
 
Protocol Buffer 序列化速度非常快,字节序列较小,但是可读性较差 。
 
一般分布式服务框架会内置多种序列化协议可供选择,如 Dubbo 支持的 7 种协议用到的序列化技术就不完全相同 。
 
服务调用
 
本地环境下,使用某个接口很简单,直接调用就行 。分布式环境下就不是那么简单了,消费者方只会存在接口的定义,没有具体的实现 。
 
想要像本地环境下直接调用远程接口那就得耗费一些功夫了,需要用到远程代理 。
 
下面是我盗的图:

这可能是讲分布式系统最到位的一篇文章

文章插图
 
远程代理
 
通信时序如下:
这可能是讲分布式系统最到位的一篇文章

文章插图
 
通信时序
 
消费者端没有具体的实现,需要调用接口时动态的去创建一个代理类 。与 Spirng 集成的情况,那直接在 Bean 构建的时候注入代理类 。
 
下面是构建代理类:
importjava.lang.reflect.Proxy;publicclassJdkProxy{publicstaticObjectgetInstance(Class<?>cls){JdkMethodProxyinvocationHandler=newJdkMethodProxy();ObjectnewProxyInstance=Proxy.newProxyInstance(cls.getClassLoader(),newClass[]{cls},invocationHandler);return(Object)newProxyInstance;}} 
importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;publicclassJdkMethodProxyimplementsInvocationHandler{@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]parameters)throwsThrowable{//如果传进来是一个已实现的具体类if(Object.class.equals(method.getDeclaringClass())){try{returnmethod.invoke(this,parameters);}catch(Throwablet){t.printStackTrace();}//如果传进来的是一个接口}else{//实现接口的核心方法//returnRemoteInvoking.invoking(serviceName,serializationType,//timeOut,loadBalanceStrategy,method,parameters);}returnnull;}} 
代理会做很多事情,对请求服务的名称及参数信息的序列化、通过路由选择最为合适服务提供者、建立通讯连接发送请求信息(或者直接发起 Http 请求)、最后返回获取到的结果 。
 
当然这里面需要考虑很多问题,如调用超时,请求异常,通讯连接的缓存,同步服务调用还是异步服务调用等等 。
 
同步服务调用:客户端发起远程服务调用请求,用户线程完成消息序列化之后,将消息投递到通信框架,然后同步阻塞,等待通信线程发送请求并接收到应答之后,唤醒同步等待的用户线程,用户线程获取到应答之后返回 。
 
异步服务调用:基于 Java 的 Future 机制,客户端发起远程服务调用请求,该请求会被标上 RequestId,同时建立一个与 RequestId 对应的 Future,客户端通过 Future 的 Get 方法获取结果时会被阻塞 。
 
服务端收到请求应达会回传 RequestId,通过 RequestId 去解除对应 Future 的阻塞,同时 Set 对应结果,最后客户端获取到结果 。


推荐阅读