截至Version06:咱们对整个过程梳理一下:
- 1.我在Client对服务器端暴露的或者说提供的服务进行调用(服务器端暴露的服务或者说提供的服务有很多个,咱以咱们例子中的这两个为例
- 比如咱们Version06中的按照id找Pilliow以及按照id找滑板,其实就是两个方法呗:getSkateboardBySkateboardId和getPilliowByPilliowId)
- 2.首先,咱们发现每次客户端调用服务器端服务(也就是那俩方法)时,我得写那些网络细节,很麻烦,所以,我搞了个Stub代理类出来,将这些处理网络细节相关的代码封装了起来并向外提供了一个getStub接口,然后我就可以像调用本地方法(服务)一样去调用远程位于服务器上的方法们(服务们)
- 3.然后,我发现,咱们不是学过动态代理嘛,那咱们这个代理类完全可以用动态代理来动态生成呀,这多灵活多方便呀
- 我只要告诉我工具类,我客户端要调服务端中哪个类中的哪个方法。并且我再调用这个服务端方法的过程中向这个服务端方法传入了哪些类型的哪几个参数过去(因为服务端帮你实现或者完成调用可以得用到这些参数,那你不给人家传过去人家咋帮你完成调用)
- 比如,我现在客户端要调用服务端的滑板类中的通过id找滑板的这个服务(服务端方法),并且为了你服务端帮我实现“通过id找滑板的这个服务(服务端方法)”,我不得不向服务端传过去一个int类型的id值222
- 然后就是动态代理帮咱们客户端生成一个代理类,然后代理类将“我要调用这个滑板类中的“通过id找滑板的这个服务(服务端方法)””“,这些信息传给服务端,服务器读到这些信息之后按照你代理类用InvocationHandler中的invoke方法处理过的(在咱们AOP那里,不就是用InvocationHandler中的invoke方法帮咱们增强目标对象或者说被代理对象嘛,只不过这里没有很明显增强的地方或者代码而已,这里主要体现动态代理帮咱们动态生成,生成完后没有像AOP那样在invoke中对原来目标类或者说被代理类做增强而已)对应的实现类以及服务端方法,执行完之后返回最终结果,也就是一个滑板给你客户端返回过来
- 此时,网络细节、服务端某个方法改变、方法增加、实体类中属性改变或者增减…,我不用改啥,因为有动态生成的代理帮我撑腰
package entity;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
public class Pilliow implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String clothesColor;
public Pilliow(Integer id, String clothesColor) {
super();
this.id = id;
this.clothesColor = clothesColor;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getClothesColor() {
return clothesColor;
}
public void setClothesColor(String clothesColor) {
this.clothesColor = clothesColor;
}
@Override
public String toString() {
return "Pilliow [id=" + id + ", clothesColor=" + clothesColor + "]";
}
}
package entity;
import java.io.Serializable;
public class Skateboard implements Serializable{
private Integer id;
private String skateboardColor;
public Skateboard(Integer id, String skateboardColor) {
super();
this.id = id;
this.skateboardColor = skateboardColor;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getSkateboardColor() {
return skateboardColor;
}
public void setSkateboardColor(String skateboardColor) {
this.skateboardColor = skateboardColor;
}
@Override
public String toString() {
return "Skateboard [id=" + id + ", skateboardColor=" + skateboardColor + "]";
}
}
然后,小胡把id给敏小言发过去时,自己就开始阻塞等待: 什么时候把Pillow发送来呢? 什么时候把Pillow发送来呢?什么时候把Pillow发送来呢? 什么时候把Pillow发送来呢?什么时候把Pillow发送来呢?什么时候把Pillow发送来呢? … 然后敏小言那边作为服务端 ,调用服务(方法)用id查找到那个Pillow后,就开始把这个Pillow给小胡发过去。 然后小胡收到了Pillow后,就去把Pillow塞到柜子里等顾客去取就行了。 其中有些偷懒细节,看注释:
package client;
import entity.Pilliow;
import server.PilliowService;
import server.SkateboardService;
import server.Stub;
public class Client {
public static void main(String[] args) throws Exception {
SkateboardService skateboardService = (SkateboardService)Stub.getStub(SkateboardService.class);
System.out.println(skateboardService.getSkateboardBySkateboardId(222));
}
}
package server;
import entity.Pilliow;
public interface PilliowService {
Pilliow getPilliowByPilliowId(Integer id);
}
package server;
import entity.Pilliow;
public class PilliowServiceImpl implements PilliowService{
@Override
public Pilliow getPilliowByPilliowId(Integer id) {
return new Pilliow(id, "black 卫衣");
}
}
package server;
import entity.Pilliow;
import entity.Skateboard;
public interface SkateboardService {
Skateboard getSkateboardBySkateboardId(Integer id);
}
package server;
import entity.Pilliow;
import entity.Skateboard;
public class SkateboardServiceImpl implements SkateboardService{
@Override
public Skateboard getSkateboardBySkateboardId(Integer id) {
return new Skateboard(id, "black");
}
}
package server;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import entity.Pilliow;
public class Server {
private static boolean flag = true;
public static void main(String[] args) throws Exception{
ServerSocket serverSocket = new ServerSocket(2221);
while(flag){
Socket socket = serverSocket.accept();
process(socket);
socket.close();
}
}
private static void process(Socket socket) throws Exception {
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
String ClazzName = objectInputStream.readUTF();
String methodName = objectInputStream.readUTF();
Class[] parameterTypes = (Class[])objectInputStream.readObject();
Object[] args = (Object[])objectInputStream.readObject();
PilliowServiceImpl serviceImpl = new PilliowServiceImpl();
Class clazz = null;
clazz = PilliowServiceImpl.class;
Method method = clazz.getMethod(methodName, parameterTypes);
Pilliow pilliow = (Pilliow) method.invoke(clazz.newInstance(), args);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(pilliow);
objectOutputStream.flush();
}
}
package server;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
import entity.Pilliow;
public class Stub {
public static Object getStub(Class clazz){
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = new Socket("127.0.0.1", 2221);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
String clazzName = clazz.getName();
String methodName = method.getName();
Class[] parameterTypes = method.getParameterTypes();
objectOutputStream.writeUTF(clazzName);
objectOutputStream.writeUTF(methodName);
objectOutputStream.writeObject(parameterTypes);
objectOutputStream.writeObject(args);
objectOutputStream.flush();
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
Pilliow pilliow = (Pilliow)objectInputStream.readObject();
objectOutputStream.close();
socket.close();
return pilliow;
}
};
Object object = Proxy.newProxyInstance(PilliowService.class.getClassLoader(), new Class[]{PilliowService.class}, handler);
System.out.println(object.getClass().getName());
System.out.println(object.getClass().getInterfaces()[0]);
return (PilliowService)object;
}
}
Readme.txt
1.服务器开启,等着客户端连接
2.客户端连接来了之后,利用Socket的IP和端口互相连接上之后
3.首先客户端开始发自己的需求:我呢,要查询一下id=221的Pilliow对象,你小子帮我查一下吧
4.服务器说,行呀,那你把id给我发过来嘛,记住,你不转成二进制是传不过来的哦
5.客户端说:行,转成二进制放到字节数组中。服务器那边读了之后,利用这个id查到了对应的Pilliow.下来服务器就得想办法把这个Pilliow给客户端传过去
6.咱们Version01这个版本中是这样干的(本来你该传输Pilliow你就把这个对象传输过去),但是服务器端偷个懒,因为我知道Pilliow中只有倆属性,我就图方便,穿俩属性过去
7.然后,客户端,说你懒我也懒,就接了俩属性,然后在自己按照这俩属性自己造了个Pilliow对象进行使用。
这个最原始的方式非常的麻烦,缺点多多,如下:
如果我这个Pilliow里面属性很多,你不得该一大片。或者说,我想把id或者衣服颜色改改,你就要改很多部分,说明程序耦合的太死。
而且,如果除了Pilliow对象后,如果还有其他对象,我怎么办?一个对象一个对象来搞?
而且这些对象在不断演进,对象的属性在不断的增减,程序得跟着不断的动
截至Version06:咱们对整个过程梳理一下:
1.我在Client对服务器端暴露的或者说提供的服务进行调用(服务器端暴露的服务或者说提供的服务有很多个,咱以咱们例子中的这两个为例
比如咱们Version06中的按照id找Pilliow以及按照id找滑板,其实就是两个方法呗:getSkateboardBySkateboardId和getPilliowByPilliowId)
2.首先,咱们发现每次客户端调用服务器端服务(也就是那俩方法)时,我得写那些网络细节,很麻烦,所以,我搞了个Stub代理类出来,将这些处理网络细节相关的代码封装了起来并向外提供了一个getStub接口,然后我就可以像调用本地方法(服务)一样去调用远程位于服务器上的方法们(服务们)
3.然后,我发现,咱们不是学过动态代理嘛,那咱们这个代理类完全可以用动态代理来动态生成呀,这多灵活多方便呀
我只要告诉我工具类,我客户端要调服务端中哪个类中的哪个方法。并且我再调用这个服务端方法的过程中向这个服务端方法传入了哪些类型的哪几个参数过去(因为服务端帮你实现或者完成调用可以得用到这些参数,那你不给人家传过去人家咋帮你完成调用)
比如,我现在客户端要调用服务端的滑板类中的通过id找滑板的这个服务(服务端方法),并且为了你服务端帮我实现“通过id找滑板的这个服务(服务端方法)”,我不得不向服务端传过去一个int类型的id值222
然后就是动态代理帮咱们客户端生成一个代理类,然后代理类将“我要调用这个滑板类中的“通过id找滑板的这个服务(服务端方法)””“,这些信息传给服务端,服务器读到这些信息之后按照你代理类用InvocationHandler中的invoke方法处理过的(在咱们AOP那里,不就是用InvocationHandler中的invoke方法帮咱们增强目标对象或者说被代理对象嘛,只不过这里没有很明显增强的地方或者代码而已,这里主要体现动态代理帮咱们动态生成,生成完后没有像AOP那样在invoke中对原来目标类或者说被代理类做增强而已)对应的实现类以及服务端方法,执行完之后返回最终结果,也就是一个滑板给你客户端返回过来
此时,网络细节、服务端某个方法改变、方法增加、实体类中属性改变或者增减...,我不用改啥,因为有动态生成的代理帮我撑腰
上面说到了咱们找到不管是滑板对象还是Pilliow对象之后,都会经过远程的网络传输:服务器端<------>客户端,那肯定要经过序列化咯。咱们用RPC时首先得把方法的参数、返回值这一类的东西给序列化为二进制,这是所有的远程调用不可或缺的一步。
而咱们目前用的序列化方式是Java自带的JDK的Serializable,这个序列化方式有下面几个缺点:
- 这个序列化方式只支持Java语言
- 很低效
- 序列化完了之后的长度还特别长
所以Version07应运而生—>RPC的序列化框架 …未完待续
|