发表于: 2021-10-28 23:36:22
2 936
一,今天完成的事情
任务八
1,任务八深度思考
1,一般指java RMI。The Java Remote Method Invocation (RMI) system allows an object running in one Java virtual machine to invoke methods on an object running in another Java virtual machine. RMI provides for remote communication between programs written in the Java programming language.
2,RMI 支持存储于不同地址空间的程序级对象之间彼此进行通信,实现远程对象之间的无缝远程调用。
如果用Spring集成RMI实现比较简单。如果使用传统的RMI来发布服务,在服务实现类中所有方法都得抛出 java.rmi.RemoteException 异常。但如果使用Spring的 org.springframework.remoting.rmi.RmiServiceExporter 将该类转化为 RMI 服务,那么实现将简单的多。 RmiServiceExporter 可以将任何一个 Spring 管理的Bean发布为一个 RMI 服务,默认情况下,RmiServiceExporter会尝试将一个RMI注册表绑定到本机的1099端口。如果在这个端口没有发现RMI注册表,RmiServiceExporter将重新启动一个注册表。如果希望将某个 RMI 注册表绑定到不同的端口或主机,可以通过 registryPort和registryHost属性指定。
2.部署两台Service,如何在WEB中随机访问任意一台Service?
1,确保能访问两台service。
2,设计随机算法,按照百分比设计访问。推荐使用负载均衡各种算法。
3,如果只有两台service且是平均概率,可以设置随机数字双数个,如果余数是0,就是访问第一台,如果余数是1,就访问第二台服务器。
3.RMI的简单介绍
希望介绍的是Remote Method Invocation的缩写RMI的大致工作原理。
1,服务器端。
提供服务,服务中要暴露可以调用的远程方法,以接口的形式表现,这样在客户端可以通过服务接口来调用远程方法,实现复杂的业务逻辑。在服务器端,首先要对接口中提供的方法实现,以便客户端调用能够完成一定的业务逻辑;接着需要生成Skeleton,在Skeleton中真正地实现了对商业方法的调用,完成了客户请求的调用的过程,将获取到的调用方法的结果通过序列化机制返回给客户端,进行应答。
2,客户端。
通过Stub来接收服务器返回的数据(对象),即在这里进行了反序列化,也就是读取网络传输的字节流,进而进行重构。
3,在Skeleton和Stub中,都对网络通信进行了处理,例如建立套接字,建立网络连接,为实际的业务需要做好准备。要将引用传递更改为值传递,也就是将对象序列化为字节,然后使用该字节的副本在客户端和服务器之间传递。
4,远程对象的发现问题:在调用远程对象的方法之前需要一个远程对象的引用,如何获得这个远程对象的引用在RMI中是一个关键的问题,如果将远程对象的发现类比于IP地址的发现可能比较好理解一些。在我们日常使用网络时,基本上都是通过域名来定位一个网站,但是实际上网络是通过IP地址来定位网站的,因此其中就需要一个映射的过程,域名系统(DNS)就是为了这个目的出现的,在域名系统中通过域名来查找对应的IP地址来访问对应的服务器。那么对应的,IP地址在这里就相当于远程对象的引用,而DNS则相当于一个注册表(Registry)。而域名在RMI中就相当于远程对象的标识符,客户端通过提供远程对象的标识符访问注册表,来得到远程对象的引用。这个标识符是类似URL地址格式的:rmi://host:port/name。
1,RPC (Remote Procedure Call):远程过程调用,用于一个进程调用另一个进程中的过程,从而提供了过程的分布能力。
2,RMI(Remote Method Invocation):远程方法调用,即在RPC的基础上有向前迈进了一步,提供分布式对象间的通讯。允许运行在一个java 虚拟机的对象调用运行在另一个java虚拟机上对象的方法。这两个虚拟机可以是运行在相同计算机上的不同进程中,也可以是运行在网络上的不同计算机中。
3,区别
1)方法调用方式不同。
RMI中是通过在客户端的Stub对象作为远程接口进行远程方法的调用。每个远程方法都具有方法签名。如果一个方法在服务器上执行,但是没有相匹配的签名被添加到这个远程接口(stub)上,那么这个新方法就不能被RMI客户方所调用;
RPC中是通过网络服务协议向远程主机发送请求,请求包含了一个参数集和一个文本值,通常形成“classname.methodname(参数集)”的形式。RPC远程主机就去搜索与之相匹配的类和方法,找到后就执行方法并把结果编码,通过网络协议发回。
2)适用语言范围不同。RMI只用于Java;RPC是网络服务协议,与操作系统和语言无关。
3)调用结果的返回形式不同。
Java是面向对象的,所以RMI的调用结果可以是对象类型或者基本数据类型。
RMI的结果统一由外部数据表示 (External Data Representation, XDR) 语言表示,这种语言抽象了字节序类和数据类型结构之间的差异。
5.Service和Service之间可以互相调用吗?是应该统一Controller调用Service,还是应该Service调用Service?
1,一般我不会用service层调用service层。
2,一般应该不同层次调用合理,也是设计层次的一个目的。应该是controller层才调用service层。
3,避免相互依赖问题,但是两个Service互相依赖会导致容器抛出异常,因为容器无法知道先加载哪个。同层级的如果互相调用,很可能应该加层。
4,事务问题难把握,事务一般情况下都是在Service层处理,本来应该controller调用顺序能分离的事务问题的揉在一团。
6.Service对外暴露的接口粒度应该是怎么样的,是只提供基础的CRUD服务,还是应该将业务逻辑包含进去?
1,只包含基础CRUD服务,每个只对一个实体类的是DAO层。不是service层。,
2,Service层处理部分业务逻辑。Service层处理包含0到多个POJO对象(即对多个表的数据操作)的业务,到Service层才判断调用哪个DAO,是业务逻辑。
3,进行事务管理(声明式事务管理)是业务逻辑。Service层(其接口的实现类)可以被注入0到多个DAO对象,以完成其数据操作。
Service层应该包含这一层应该有的业务逻辑。
7.Thrift,Protobuffer分别是什么,一般用于什么场景?
1,Thrift提供RPC服务框架,提供多语言的编译功能,并提供多种服务器工作模式。目前是 Apache软件基金会的开源项目。用户通过Thrift的IDL(接口定义语言)来描述接口函数及数据类型,然后通过Thrift的编译环境生成各种语言类型的接口文件,用户可以根据自己的需要采用不同的语言开发客户端代码和服务器端代码。
2,Thrift适用于C# 、 C++ (基于 POSIX 兼容系统)、Cappuccino、 Cocoa 、 Delphi 、 Erlang 、 Go 、 Haskell 、 Java 、 Node.js 、 OCaml 、 Perl 、 PHP 、 Python 、 Ruby 和 Smalltalk不同语言之间RPC调用。
3,Protocol Buffer是一种支持多平台、多语言、可扩展的的数据序列化机制,相较于XML来说,protobuf更小更快更简单,支持自定义的数据结构,用protobuf编译器生成特定语言的源代码,如C++、Java、Python,目前protoBuf对主流的编程语言都提供了支持,非常方便的进行序列化和反序列化。
4,Google Developers: Protocol buffers currently support generated code in Java, Python, Objective-C, and C++. With our new proto3 language version, you can also work with Kotlin, Dart, Go, Ruby, and C#, with more languages to come.提供这些语言的序列化和反序列化服务。序列化后的数据相对小,节约带宽,同样带宽速度快。节约带宽可能就节约钱。如果是上述语言,这个服务可能是序列化反序列化文件最小的,考虑使用。除了RPC,还有存储,传输等场景可用。
8.什么是序列化和反序列化,在RMI中是否要实现 Serializable 接口, serialVersionUID的用处是什么?
1,seriallization 序列化 : 将对象转化为便于传输的格式, 常见的序列化格式:二进制格式,字节数组,json字符串,xml字符串。
deseriallization 反序列化:将序列化的数据恢复为对象的过程。
2,需要。RMI远程方法调用最底层是通过socket通信来实现对象的传输,其数据的传输最后都归结于二进制的数据包,对象一定要转成二进制格式,就是需要序列化和反序列化。所以通过RMI传输对象时,一定需要实现Serializable接口,否则程序不能正常运行。
3,serialVersionUID是序列化与反序列化过程中的类信息校验值,是用于在序列化和反序列化过程中进行核验的一个版本号。Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。
9.Controller通过RMI调用服务是否有延迟?10条Long型的ID循环调用1000次,和本地调用之间的时间相差多少?
1,Controller通过RMI调用服务相对直接调用,在需要通过网络的时候有延迟。需要通过网络传递client和server之间的信息,controller有去服务器和从服务器回来的网络延迟。
2,10条Long型的ID循环调用1000次:应该是按id查找,10条,1000次。这和“controller去服务器和从服务器回来的网络延迟”的平均网络延迟时间有关,和设计也有关系。
1)如果有一个业务,每次要查询的都是10个那么多id(每次id可以不一样),按id集中一次查询,我可以用array, List等等把ID传过去,这样每10个ID只有一个来回,只有传输1000次。
2)如果每个ID传输一次,就是查询不集中,那就是查询 10*1000=10000次。
3)本地可以在调用远程Service前查看缓存,NoSQL等,在controller使用Stub前拦截查询请求。
4)对象数据序列化1个和10个大小差别不大。不用本地NoSQL等cache的情况,相对本地Controller调用Service时间的差别是 平均“controller去服务器和从服务器回来的网络延迟”*远程调用次数。
2,任务八任务小结
任务八小结:
1,任务八要求使用RMI,把Web端和Service端分离。拆分出RMI的service。
2,代码按照RMI的设计,在web端调用的是远端service。
3,所以service以及以下,是service端。controller以及以上是web端,就是client端。
4,按照要求有两台service,做到随机同等概率访问任意一台service,客户不会看到甩出的错误。是通过web部分,具体是我在controller中调用相关代码决定访问哪一个service。
5,通过Nginx配置两台WEB容器,达到随机访问的目的。所以我的client代码是写一份,但是client.war是需要部署在不同的WEB容器中。其实就是把两个同样的client.war粘贴到两个不同的WEB容器中,用网址能访问,通过设置能访问到war包的地方。
6,达到了要求:任何一台WEB/Service 挂掉,不影响正常使用。
问题,困惑,疑难:
NoSQL在web和service拆分的时候处理方式,序列化和反序列化。
我在选择service的解决方案中,没有使用负载均衡工具算法。没有使用heart beat,是操作看看是否抛出错误,转换到另外一个service。
在云Linux上,我的client.war(包含web部分的包)通过RMI访问service非常慢。
二,今天问题
我的jar包 和war包,在cmd中的代码启动jar,在本地配合运行速度能在1s内反应。但是在云Linux上,RMI需要网络传输,速度慢,10s反应速度都算快。
三,今天的收获
分布式系统RMI问题。提交任务八,去掉了其中的properties文件。
四,明天的计划
任务九
评论