RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议。它允许像调用本地服务一样调用远程服务。它可以有不同的实现方式。如RMI(远程方法调用)、Hessian、Http invoker等。另外,RPC是与语言无关的。
RPC示意图
如上图所示,假设Computer1在调用sayHi()方法,对于Computer1而言调用sayHi()方法就像调用本地方法一样,调用 –>返回。但从后续调用可以看出Computer1调用的是Computer2中的sayHi()方法,RPC屏蔽了底层的实现细节,让调用者无需关注网络通信,数据传输等细节。
上面介绍了RPC的核心原理:RPC能够让本地应用简单、高效地调用服务器中的过程(服务)。它主要应用在分布式系统。如Hadoop中的IPC组件。但怎样实现一个RPC框架呢?
从下面几个方面思考,仅供参考:
1.通信模型:假设通信的为A机器与B机器,A与B之间有通信模型,在Java中一般基于BIO或NIO;。
2.过程(服务)定位:使用给定的通信方式,与确定IP与端口及方法名称确定具体的过程或方法;
3.远程代理对象:本地调用的方法(服务)其实是远程方法的本地代理,因此可能需要一个远程代理对象,对于Java而言,远程代理对象可以使用Java的动态对象实现,封装了调用远程方法调用;
4.序列化,将对象名称、方法名称、参数等对象信息进行网络传输需要转换成二进制传输,这里可能需要不同的序列化技术方案。如:protobuf,Arvo等。
下面使用比较原始的方案实现RPC框架,采用Socket通信、动态代理与反射与Java原生的序列化。
RPC架构分为三部分:
1)服务提供者,运行在服务器端,提供服务接口定义与服务实现类。
2)服务中心,运行在服务器端,负责将本地服务发布成远程服务,管理远程服务,提供给服务消费者使用。
3)服务消费者,运行在客户端,通过远程代理对象调用远程服务。
3、 具体实现
分层
dao 接口和接口实现类
service 服务器端
Util 工具
client 客户端
test 测试类
model 业务逻辑
user.java
package ch09.model;
import java.io.Serializable;
public class User implements Serializable{
private String userName;
private String passWord;
private String nickName;
private int age;
public User() {
super();
}
public User(String userName, String passWord, String nickName, int age) {
super();
this.userName = userName;
this.passWord = passWord;
this.nickName = nickName;
this.age = age;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User [userName=" + userName + ", passWord=" + passWord + ", nickName=" + nickName + ", age=" + age
+ "]";
}
}
UserResult.java
package ch09.model;
import java.io.Serializable;
public class UserResult implements Serializable{
//是否登录成功
private boolean result;
//描述信息
private String dec;
public boolean isResult() {
return result;
}
public void setResult(boolean result) {
this.result = result;
}
public String getDec() {
return dec;
}
public void setDec(String dec) {
this.dec = dec;
}
@Override
public String toString() {
return "UserResult [result=" + result + ", dec=" + dec + "]";
}
}
Response.java
package ch09.model;
import java.io.Serializable;
/**
* 得到返回数据
* 1:返回状态码
* 2:返回的结果 ----->userResult
* 3:返回异常信息
* @author Administrator
*
*/
public class Response implements Serializable{
private String status;
private UserResult result;
private Exception e;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public UserResult getResult() {
return result;
}
public void setResult(UserResult result) {
this.result = result;
}
public Exception getE() {
return e;
}
public void setE(Exception e) {
this.e = e;
}
@Override
public String toString() {
return "Response [status=" + status + ", result=" + result + ", e=" + e + "]";
}
}
Request.java
package ch09.model;
import java.io.Serializable;
import java.util.Arrays;
/**
* 发送的请求
* 1:类名
* 2:方法名
* 3:参数类型
* 4:参数
* @author Administrator
*
*/
public class Request implements Serializable{
private String className;
private String methodName;
private Class[] paramTypes;
private Object[] paramValues;
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Class[] getParamTypes() {
return paramTypes;
}
public void setParamTypes(Class[] paramTypes) {
this.paramTypes = paramTypes;
}
public Object[] getParamValues() {
return paramValues;
}
public void setParamValues(Object[] paramValues) {
this.paramValues = paramValues;
}
@Override
public String toString() {
return "Request [className=" + className + ", methodName=" + methodName + ", paramTypes="
+ Arrays.toString(paramTypes) + ", paramValues=" + Arrays.toString(paramValues) + "]";
}
}
UserService.java
package ch09.server;
import ch09.model.User;
import ch09.model.UserResult;
/**
* 用户登录和注册
* @author Administrator
*
*/
public interface UserService {
UserResult login(User user);
UserResult register(User user);
}
UserServiceImpl.java
package ch09.server;
import ch09.dao.UserDao;
import ch09.dao.impl.UserDaoImpl;
import ch09.model.User;
import ch09.model.UserResult;
import ch09.server.UserService;
public class UserServiceImpl implements UserService{
@Override
public UserResult login(User user) {
//保存返回数据的
UserResult result = new UserResult();
UserDao dao = new UserDaoImpl();
//通过用户名从文件中取到的
User userByName = dao.getUserByName(user.getUserName());
if(userByName==null){//没有找到该用户
result.setResult(false);
result.setDec("没有该用户");
}else{
if(userByName.getPassWord().equals(user.getPassWord())){
result.setResult(true);
result.setDec("登录成功");
}else{
result.setResult(false);
result.setDec("用户名密码不匹配");
}
}
return result;
}
@Override
public UserResult register(User user) {
UserResult result = new UserResult();
UserDao dao = new UserDaoImpl();
User userByName = dao.getUserByName(user.getUserName());
if(userByName==null){//没有人注册过这个名字
if(dao.save(user)){
result.setResult(true);
result.setDec("注册成功");
}else{
result.setResult(false);
result.setDec("未知异常,注册失败");
}
}else{
result.setResult(false);
result.setDec("名字被占用");
}
return result;
}
}
Const.java
package ch09.util;
/**
* 存放常量
*
*/
public class Const {
public static final String FILE_PATH = "E:\\data\\userMapFile.txt";
}
FileUtils.java
package ch09.util;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Map;
import ch09.model.User;
/**
* 读取文件和保存文件
* map
* @author Administrator
*
*/
public class FileUtiles {
/**
* 保存对象到文件中
* @param map
*/
public static void saveMapToFile(Map map){
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(Const.FILE_PATH));
oos.writeObject(map);
oos.flush();
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取所有user 获取map
* @return
*/
public static Map getUserMapByFile(){
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Const.FILE_PATH));
Object readObject = ois.readObject();
Map map = (Map)readObject;
return map;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
Server.java
package ch09;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import ch09.model.Request;
import ch09.model.Response;
import ch09.model.UserResult;
/**
* 发布服务
* @author Administrator
*
*/
public class Server {
public static void main(String[] args) {
try {
ServerSocket ssc = new ServerSocket(9999);
while(true){
//获取客户端的链接
Response res = new Response();
Socket accept = ssc.accept();
ObjectInputStream ois = new ObjectInputStream(accept.getInputStream());
//接收到对象
Object readObject = ois.readObject();
Request req = (Request)readObject;
String className = req.getClassName();
String methodName = req.getMethodName();
Class[] paramTypes = req.getParamTypes();
Object[] paramValues = req.getParamValues();
//反射
//获取class对象
Class> forName = Class.forName(className);
//根据class进行实例化
Object newInstance = forName.newInstance();
//获取方法对象
Method method = forName.getMethod(methodName, paramTypes);
//执行方法
Object invoke = method.invoke(newInstance, paramValues);
UserResult result = (UserResult)invoke;
res.setStatus("200");
res.setResult(result);
//得到输出流,把数据发送给客户端
ObjectOutputStream oos = new ObjectOutputStream(accept.getOutputStream());
//把数据发送出去
oos.writeObject(res);
oos.flush();
oos.close();
ois.close();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Client.java
package ch09.clinet;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import ch09.model.Request;
import ch09.model.Response;
import ch09.model.User;
/**
* 客户端
* @author Administrator
*
*/
public class Client {
public static void main(String[] args) {
try {
Socket sc = new Socket("localhost", 9999);
ObjectOutputStream oos = new ObjectOutputStream(sc.getOutputStream());
//发送数据的封装
Request req = new Request();
req.setClassName("cn.edu360.day09.service.impl.UserServiceImpl");
req.setMethodName("register");
req.setParamTypes(new Class[]{User.class});
req.setParamValues(new Object[]{new User("mingming", "1234", null, 0)});
//发送数据
oos.writeObject(req);
oos.flush();
ObjectInputStream ois = new ObjectInputStream(sc.getInputStream());
//接收数据
Object readObject = ois.readObject();
Response res = (Response)readObject;
System.out.println(res);
sc.shutdownInput();
sc.shutdownOutput();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
UserDaoImpl.java
package ch09.dao.impl;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import ch09.dao.UserDao;
import ch09.model.User;
import ch09.util.Const;
import ch09.util.FileUtiles;
public class UserDaoImpl implements UserDao{
@Override
public boolean save(User user) {
File file = new File(Const.FILE_PATH);
Map map = null;
if(file.exists()){//当文件存在的时候,不是第一个保存的用户
map = FileUtiles.getUserMapByFile();
map.put(user.getUserName(), user);
}else{//之前没有文件
map = new HashMap<>();
map.put(user.getUserName(), user);
}
//把所有的用户保存回去
FileUtiles.saveMapToFile(map);
return true;
}
@Override
public User getUserByName(String userName) {
File file = new File(Const.FILE_PATH);
if(file.exists()){
Map userMapByFile = FileUtiles.getUserMapByFile();
User user = userMapByFile.get(userName);
return user;
}
return null;
}
}
UserDao.java
package ch09.dao;
import ch09.model.User;
public interface UserDao {
/**
* 保存user对象
* @param user
* @return
*/
boolean save(User user);
/**
* 通过用户名获取用户对象
* @param userName
* @return
*/
User getUserByName(String userName);
}
TestFileUtile.java
package ch09.test;
/*
*
* 测试类
*
*
*/
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import ch09.dao.UserDao;
import ch09.dao.impl.UserDaoImpl;
import ch09.model.User;
import ch09.model.UserResult;
import ch09.server.UserService;
/*import ch09.server.UimplserService;
*/import ch09.server.UserServiceImpl;
import ch09.util.FileUtiles;
public class TestFileUtile {
@Test
public void testFileUtile(){
Map map = new HashMap<>();
map.put("mingming", new User("mingming","123","明明",18));
FileUtiles.saveMapToFile(map);
Map userMapByFile = FileUtiles.getUserMapByFile();
System.out.println(userMapByFile);
}
@Test
public void testUserDao(){
UserDao dao = new UserDaoImpl();
//dao.save(new User("lainglaing", "123", "亮亮", 19));
User userByName = dao.getUserByName("mingming");
System.out.println(userByName);
}
@Test
public void testUserService(){
UserService service =new UserServiceImpl();
UserResult result1 = service.login(new User("mingmin", "1234", null, 0));
System.out.println(result1);
UserResult result2 = service.register(new User("honghong", "123", "红红", 18));
System.out.println(result2);
}
}