第三部分:分布式系统网络通信
SpringCloudAlibaba微服务实战教程系列
第一部分第二部分:分布式架构基础、Paxos算法、Raft算法、系统网络通信
第三部分:分布式系统网络通信
一、网络通信基础
计算机系统网络通信的基本原理,在底层层面去看,网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络IO来实现,其中传输协议包络:tcp、udp等等,tcp、udp都是在基于Socket概念上为某类应用场景而扩展出的传输协议,网络IO主要包括:BIO、NIO、AIO三种方式,所有的分布式应用通讯都基于这个原理而实现,只是为了应用的易用,各种语言通常都会提供一些更为贴近应用易用的应用层协议。
二、什么是RPC
RPC全称为remote procedure call,即远程过程调用。并非是一种通信协议,是一种思想或者说是一类通信协议的总称。借助RPC可以做到像本地调用一样调用远程服务,是一种进程间的通信方式。无论是何种类型的数据,最终都需要转换成二进制流在网络上进行传输,数据的发送方需要将对象转换为二进制流(也称为序列化),而数据的接收方则需要把二进制流再恢复为对象(称为反序列化)。在java中RPC框架比较多,常见的有Hessian、gRPC、Thrift、HSF (High Speed Service Framework)、Dubbo等,其实对于RPC框架而言,核心模块就是通讯和序列化。
三、通信基础RMI
Java RMI,即 远程方法调用(Remote Method Invocation),一种用于实现远程过程调用(RPC)(Remote procedure call)的Java API, 能直接传输序列化后的Java对象和分布式垃圾收集。它的实现依赖于Java虚拟机(JVM),因此它仅支持从一个JVM到另一个JVM的调用。
客户端:
1)存根/桩(Stub):远程对象在客户端上的代理;
2)远程引用层(Remote Reference Layer):解析并执行远程引用协议;
3)传输层(Transport):发送调用、传递远程方法参数、接收远程方法执行结果。
服务端:
1)骨架(Skeleton):读取客户端传递的方法参数,调用服务器方的实际对象方法,
并接收方法执行后的返回值;
2)远程引用层(Remote Reference Layer):处理远程引用后向骨架发送远程方法调用;
3)传输层(Transport):监听客户端的入站连接,接收并转发调用到远程引用层。
1)客户端从远程服务器的注册表中查询并获取远程对象引用。 2)桩对象与远程对象具有相同的接口和方法列表,当客户端调用远程对象时,实际上是由相应的桩对象代理完成的。 3)远程引用层在将桩的本地引用转换为服务器上对象的远程引用后,再将调用传递给传输层(Transport),由传输层通 过TCP协议发送调用; 4)在服务器端,传输层监听入站连接,它一旦接收到客户端远程调用后,就将这个引用转发给其上层的远程引用层; 5)服务器端的远程引用层将客户端发送的远程应用转换为本地虚拟机的引用后,再将请求传递给骨架(Skeleton); 6)骨架读取参数,又将请求传递给服务器,最后由服务器进行实际的方法调用。
RMI代码实现项目结构
1、公用实体类
package city.albert; import java.io.Serializable; /** * @author niunafei * @function * @email niunafei0315@163.com * @date 2020/7/23 2:16 PM */ public class UserInfo implements Serializable { private String name; private int age; public UserInfo(String name, int age) { this.name = name; this.age = age; } public UserInfo() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "UserInfo{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
2、公用接口
package city.albert; import java.rmi.Remote; import java.rmi.RemoteException; /** * @author niunafei * @function * @email niunafei0315@163.com * @date 2020/7/23 2:18 PM */ public interface UserService extends Remote { /** * rmi注意重点: * 第一:接口类上继承 Remote接口 * 第二:实现类上继承UnicastRemoteObject 类 * 第三:调用方法上抛出RemoteException异常 * 第四:返回对象实现序列化接口Serializable * * @param name * @return * @throws RemoteException */ UserInfo getUserInfo(String name) throws RemoteException; }
3、接口实现类
package city.albert; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; /** * @author niunafei * @function * @email niunafei0315@163.com * @date 2020/7/23 2:18 PM */ public class UserServiceImpl extends UnicastRemoteObject implements UserService { /** * 因为UnicastRemoteObject的构造方法抛出了RemoteException异常,因此这里默认的构造方法必须写,必须 声明抛出RemoteException异常 * * @throws RemoteException */ protected UserServiceImpl() throws RemoteException { } @Override public UserInfo getUserInfo(String name) throws RemoteException { System.out.println("name=" + name); return new UserInfo(name, 18); } }
4、服务端注册启动注册
package city.albert; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; /** * @author niunafei * @function * @email niunafei0315@163.com * @date 2020/7/23 2:19 PM */ public class Application { public static void main(String[] args) throws RemoteException { //创建注册实例 UserService service = new UserServiceImpl(); //创建注册表对象绑定端口 Registry registry = LocateRegistry.createRegistry(8088); /** * //绑定的URL标准格式为:rmi://host:port/name(其中协议名可以省略,下面两种写法都是正确的) * 第一种:rmi://host:port/name * 第二种://host:port/name */ try { //绑定注册实例 Naming.bind("rmi://127.0.0.1:8088/user", service); System.out.println("注册成功"); } catch (Exception e) { e.printStackTrace(); } } }
5、客户端启动调用
package city.albert; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; /** * @author niunafei * @function * @email niunafei0315@163.com * @date 2020/7/23 2:26 PM */ public class CliApplication { public static void main(String[] args) { try { //获取注册的注册对象 UserService service = (UserService) Naming.lookup("rmi://127.0.0.1:8088/user"); //通过动态代理对象调用方法 UserInfo info = service.getUserInfo("张三"); System.out.println(info); } catch (NotBoundException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } } }
客户端结果打印
四、IO模型:BIO、AIO、NIO
1、同步异步:是指应用程序与内核之间的通信方式,同步是用户进程出发IO操作或者轮询方式判断读写操作是否就绪,异步是通过事件通知是否就绪。
2、BIO同步阻塞IO模型,就是服务器模式为一个连接一个线程
BIO、AIO、NIO:https://www.imooc.com/article/265871
五、netty