一、
RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议
二、
1.序列化与反序列化
序列化:将对象从内存中以二进制数据流的形式传递
反序列化:将二进制数据传递到内存中
持久化:内存中的数据是不能永久保存的,即瞬时数据,保存到文本中或其他介质中,永久化
反持久化:从持久化转化为内存存储
持久化的前提是序列化,但序列化的目的不一定是为了持久化
2.基本实现
若:
(若实现Serializable ,生成的
private static final long serialVersionUID = 1L;
)
执行序列化后,修改了Object的属性,增加或删除,再次执行反序列化的时候,异常
解释:
序列化写入的类与反序列化读出的类是两个不同的类,
是因为作用域的原因,写入时,程序执行完毕,内存自动回收
可以认为 读入的类是写入的类的克隆,具有相同的内容
若属性的数量或内容对应不上,克隆就会出现异常
对象序列化即实现Serializable 后,会生成一个随机的serialVersionUID
若属性变化,则会生成新的 serialVersionUID,
进行序列化读出与写入的时候就会比较serialVersionUID是否是同一个
即 serialVersionUID 是序列化与反序列化时的唯一标识
其他:
RPC传输Object时,设置不同机器上的Person的serialVersionUID相同,
即使Person中的内容完全相同,也不一定认为是同一个,即修改serialVersionUID,保持一致
3.transient 不需序列化
输出:
zhang null male
age 属性不需序列化,所以无输出
4.
缺点:
1)只支持java语言,无法跨语言
2)序列化后的数据量比较大
3)执行序列化与反序列化的效率比较低
三、第三方序列化与反序列化框架
Avro
GoogleProtoBuffer
Thrift
1.GoogleProtoBuffer
生成与执行步骤
1)数据写入 .proto文件
2)编译成类文件
3)导入类文件进行序列化与反序列化
1)PersonProto.proto
2.编译类文件
解压:protoc.exe 安装包,见 下方
打开 win+r cmd --> cd .. 到安装包目录 --> 将上面的 PersonProto.proto 拷贝到该目录下
执行 protoc.ext --java_out . ./PersonProto.proto
[img]http://dl2.iteye.com/upload/attachment/0124/5052/c09b41c6-d91a-3602-8fdc-21917ab98614.png[/img]
在解压的目录中会出现相应的生成文件
[img]http://dl2.iteye.com/upload/attachment/0124/5054/6b1879c0-e2c3-34ef-a71f-d2279c9dee1e.png[/img]
拷贝文件至对应的包中
报错--因为未导入相应的JAR包 -- 见下面链接
3.调用API执行
序列化后的文件大小对比
[img]http://dl2.iteye.com/upload/attachment/0124/5057/b3fc5410-f6eb-37ae-bb42-c26b30fc9393.png[/img]
[img]http://dl2.iteye.com/upload/attachment/0124/5059/3a3ab82d-a675-3cc7-8c5f-e883863305c3.png[/img]
4.扩展
若IO流改为网络流,
接收方以同样的方式对 PersonProto.proto 进行处理,语言可以不同,即不同语言间传递对象
三、RPC实现
描述:客户端发送数据,服务端接收,计算,返回结果给客户端
1.MathService.proto
生成对应的JAVA文件
2.
RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议
二、
1.序列化与反序列化
序列化:将对象从内存中以二进制数据流的形式传递
反序列化:将二进制数据传递到内存中
持久化:内存中的数据是不能永久保存的,即瞬时数据,保存到文本中或其他介质中,永久化
反持久化:从持久化转化为内存存储
持久化的前提是序列化,但序列化的目的不一定是为了持久化
2.基本实现
public class Person implements Serializable{
/**
*
*/
private static final long serialVersionUID = -1170943594852852868L;
private String name ;
private Integer age ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Person(String name, Integer age) {
super();
this.name = name;
this.age = age;
}
public Person() {
super();
}
}
/**
* 序列化:将对象以二进制的格式输出到文本文件中
* @throws IOException
*/
@Test
public void testSerializable() throws IOException{
/**
* 所有需要序列化的类均需要实现 Serializable 接口
* 标记接口,标识 此类允许序列化与反序列化
* java.io.NotSerializableException
*/
Person person = new Person();
person.setName("zhang");
person.setAge(Integer.valueOf(26));
// 序列化输出:文件内容为二进制格式,无法直接阅读
FileOutputStream out = new FileOutputStream("D:\\study\\test.txt");
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(person);
oos.flush();
oos.close();
}
/**
* 反序列化:读取文本中的二进制信息
* @throws IOException
* @throws ClassNotFoundException
*/
@Test
public void testReverseSerializable() throws IOException, ClassNotFoundException{
FileInputStream in = new FileInputStream("D:\\study\\test.txt");
ObjectInputStream ois = new ObjectInputStream(in);
Person person = (Person) ois.readObject();
System.out.println(person.getName()+" "+person.getAge());
}
若:
(若实现Serializable ,生成的
private static final long serialVersionUID = 1L;
)
执行序列化后,修改了Object的属性,增加或删除,再次执行反序列化的时候,异常
解释:
序列化写入的类与反序列化读出的类是两个不同的类,
是因为作用域的原因,写入时,程序执行完毕,内存自动回收
可以认为 读入的类是写入的类的克隆,具有相同的内容
若属性的数量或内容对应不上,克隆就会出现异常
对象序列化即实现Serializable 后,会生成一个随机的serialVersionUID
若属性变化,则会生成新的 serialVersionUID,
进行序列化读出与写入的时候就会比较serialVersionUID是否是同一个
即 serialVersionUID 是序列化与反序列化时的唯一标识
其他:
RPC传输Object时,设置不同机器上的Person的serialVersionUID相同,
即使Person中的内容完全相同,也不一定认为是同一个,即修改serialVersionUID,保持一致
3.transient 不需序列化
public class Person implements Serializable{
/**
*
*/
private static final long serialVersionUID = -1170943594852852868L;
private String name ;
/**
* 若某个属性保密,不希望进行系列化处理,加关键字,transient
*/
private transient Integer age ;
private String gender ;
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Person(String name, Integer age) {
super();
this.name = name;
this.age = age;
}
public Person() {
super();
}
public Person(String name, Integer age, String gender) {
super();
this.name = name;
this.age = age;
this.gender = gender;
}
}
/**
* 序列化:将对象以二进制的格式输出到文本文件中
* @throws IOException
*/
@Test
public void testSerializable() throws IOException{
/**
* 所有需要序列化的类均需要实现 Serializable 接口
* 标记接口,标识 此类允许序列化与反序列化
* java.io.NotSerializableException
*/
Person person = new Person();
person.setName("zhang");
person.setAge(Integer.valueOf(26));
person.setGender("male");
// 序列化输出:文件内容为二进制格式,无法直接阅读
FileOutputStream out = new FileOutputStream("D:\\study\\test.txt");
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(person);
oos.flush();
oos.close();
}
/**
* 反序列化:读取文本中的二进制信息
* @throws IOException
* @throws ClassNotFoundException
*/
@Test
public void testReverseSerializable() throws IOException, ClassNotFoundException{
FileInputStream in = new FileInputStream("D:\\study\\test.txt");
ObjectInputStream ois = new ObjectInputStream(in);
Person person = (Person) ois.readObject();
System.out.println(person.getName()+" "+person.getAge()+" "+person.getGender());
}
输出:
zhang null male
age 属性不需序列化,所以无输出
4.
缺点:
1)只支持java语言,无法跨语言
2)序列化后的数据量比较大
3)执行序列化与反序列化的效率比较低
public class PersonObjectSeriable implements Serializable{
/**
*
*/
private static final long serialVersionUID = -7328657292302834890L;
private String name ;
private transient Integer age ;
private String gender ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public PersonObjectSeriable() {
super();
}
}
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import org.junit.Test;
public class SunSerializableTest {
@Test
public void testSerializable() throws IOException{
PersonObjectSeriable person = new PersonObjectSeriable();
person.setName("zhang");
person.setAge(26);
person.setGender("male");
FileOutputStream out = new FileOutputStream("D:\\study\\java\\PersonObjectSerializable.txt");
ObjectOutputStream stream = new ObjectOutputStream(out);
stream.writeObject(person);
stream.flush();
stream.close();
}
@Test
public void testReverseSeriable() throws IOException, ClassNotFoundException{
FileInputStream in = new FileInputStream("D:\\study\\java\\PersonObjectSerializable.txt");
ObjectInputStream stream = new ObjectInputStream(in);
PersonObjectSeriable person = (PersonObjectSeriable) stream.readObject();
System.out.println(person.getName()+" "+person.getAge()+" "+person.getGender());
}
}
三、第三方序列化与反序列化框架
Avro
GoogleProtoBuffer
Thrift
1.GoogleProtoBuffer
生成与执行步骤
1)数据写入 .proto文件
2)编译成类文件
3)导入类文件进行序列化与反序列化
1)PersonProto.proto
package cn.study;// 文件的包路径,便于其他文件引用使用
option java_package = "com.study.rpc.ser.day.one.test"; // 生成的JAVA的类的包名
option java_outer_classname = "PersonProtos"; // JAVA文件名称
message Person{ // 声明类
// required 表示必须,optional 表示可选
// int32 对应 int 类型
// 1,2,3 ... 类中属性的唯一标识,按顺序添加,一旦添加不可更改;否则无法实现序列化
required string name = 1;
optional int32 age = 2;
required string gender = 3;
}
2.编译类文件
解压:protoc.exe 安装包,见 下方
打开 win+r cmd --> cd .. 到安装包目录 --> 将上面的 PersonProto.proto 拷贝到该目录下
执行 protoc.ext --java_out . ./PersonProto.proto
[img]http://dl2.iteye.com/upload/attachment/0124/5052/c09b41c6-d91a-3602-8fdc-21917ab98614.png[/img]
在解压的目录中会出现相应的生成文件
[img]http://dl2.iteye.com/upload/attachment/0124/5054/6b1879c0-e2c3-34ef-a71f-d2279c9dee1e.png[/img]
拷贝文件至对应的包中
报错--因为未导入相应的JAR包 -- 见下面链接
3.调用API执行
@Test
public void testSerializable() throws IOException{
Person person = Person.newBuilder()
.setName("zhang").setAge(26).setGender("male").build();
FileOutputStream output = new FileOutputStream("D:\\study\\java\\PersonObjectProto.txt");
person.writeTo(output);
}
序列化后的文件大小对比
[img]http://dl2.iteye.com/upload/attachment/0124/5057/b3fc5410-f6eb-37ae-bb42-c26b30fc9393.png[/img]
[img]http://dl2.iteye.com/upload/attachment/0124/5059/3a3ab82d-a675-3cc7-8c5f-e883863305c3.png[/img]
@Test
public void testReverseSerializable() throws IOException{
FileInputStream input = new FileInputStream("D:\\study\\java\\PersonObjectProto.txt");
Person person = Person.parseFrom(input);
input.close();
System.out.println(person.getName()+" "+person.getAge()+" "+person.getGender());
}
4.扩展
若IO流改为网络流,
接收方以同样的方式对 PersonProto.proto 进行处理,语言可以不同,即不同语言间传递对象
三、RPC实现
描述:客户端发送数据,服务端接收,计算,返回结果给客户端
1.MathService.proto
package cn.study;
option java_package = "com.study.rpc.ser.day.one";
option java_outer_classname = "MathServiceProto";
option java_generic_services = true ; // 是否包含服务
message Req{ // 定义第一个类,接收两个参数
required int32 num1 = 1;
required int32 num2 = 2;
}
message Resp{ // 定义第二个类,返回值
required int32 result = 1;
}
service MathService{ // 定义接口,方法名称 add 参数 Req 返回值 Resp
rpc add(Req) returns (Resp);
}
生成对应的JAVA文件
2.
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import com.google.protobuf.BlockingRpcChannel;
import com.google.protobuf.Descriptors.MethodDescriptor;
import com.google.protobuf.Message;
import com.google.protobuf.RpcController;
import com.google.protobuf.ServiceException;
import com.study.rpc.ser.day.one.MathServiceProto.MathService;
import com.study.rpc.ser.day.one.MathServiceProto.MathService.BlockingInterface;
import com.study.rpc.ser.day.one.MathServiceProto.Req;
import com.study.rpc.ser.day.one.MathServiceProto.Resp;
public class MathClient {
/**
* @param args
* @throws ServiceException
*/
public static void main(String[] args) throws ServiceException {
//stu -- 存根 -- 远程服务在本地的一个代表
//-- 具有和远程服务相同的远程调用方法
//-- 如果本地需要调用远程方法 找到存根调用对应方法即可
//-- 存根的底层会自动调用远程的方法来执行并获取结果并返回
//即:去取钱,但附近没有银行,但有ATM
//ATM就相当于银行的一个远程服务在本地的代表;取钱 扣款 与去银行是一样的
BlockingInterface stu = MathService.newBlockingStub(new MyBlockingRPCchannel());
Req req = Req.newBuilder().setNum1(25).setNum2(26).build();
Resp resp = stu.add(null, req);
System.out.println(resp.getResult());
}
}
class MyBlockingRPCchannel implements BlockingRpcChannel{
public Message callBlockingMethod(MethodDescriptor methodDescriptor,
RpcController controller, Message req, Message resp)
throws ServiceException {
Socket socket = new Socket();
// 请求连接远程服务器
try {
socket.connect(new InetSocketAddress("127.0.0.1", 9999));
// 发送请求给服务器
OutputStream out = socket.getOutputStream();
req.writeTo(out);
socket.shutdownOutput();
// 接收服务器返回结果
InputStream in = socket.getInputStream();
resp = Resp.parseFrom(in);
socket.shutdownInput();
return resp;
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null ;
}
}
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import com.google.protobuf.RpcController;
import com.google.protobuf.ServiceException;
import com.study.rpc.ser.day.one.MathServiceProto.MathService.BlockingInterface;
import com.study.rpc.ser.day.one.MathServiceProto.Req;
import com.study.rpc.ser.day.one.MathServiceProto.Resp;
public class MathService {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
ServerSocket server = null ;
Socket socket = null ;
try {
server = new ServerSocket();
server.bind(new InetSocketAddress(9999));
socket = server.accept();
InputStream input = socket.getInputStream();
Req req = Req.parseFrom(input);
socket.shutdownInput();
MyMathService mathService = new MyMathService();
Resp resp = mathService.add(null, req);
OutputStream out = socket.getOutputStream();
resp.writeTo(out);
socket.shutdownOutput();
} catch (ServiceException e) {
e.printStackTrace();
}finally{
socket.close();
server.close();
}
}
}
class MyMathService implements BlockingInterface{
public Resp add(RpcController controller, Req request)
throws ServiceException {
int num1 = request.getNum1();
int num2 = request.getNum2();
int result = num1 + num2 ;
Resp resp = Resp.newBuilder().setResult(result).build();
return resp;
}
}