举个例子,什么是rpc。
首先,A,B是两个系统相互独立的系统。
现在,B系统需要A系统的数据,一般的做法是A系统提供一个a接口,B系统通过get,post等方式,用http调用,获取A系统的数据。
rpc框架的目的就是要让两个系统的调用变得简单,B系统调用A系统,就像调用本地的类一样快捷。
我们先使用socket进行连接,如果不太熟悉socket通讯,可以看这一段精炼的代码了解一下
socket连接通讯
以上代码运行时先启动server类,再启动client类。
我们开始简单的rpc框架搭建吧。
demo预览如下:
public class ComsumerApp {
public static void main(String[] args) {
Calculator calculator = new CalculatorRemoteImpl();
int result = calculator.add(1, 2);
String str = calculator.send("hello rpc");
}
}
public class CalculatorRemoteImpl implements Calculator {
public static final int PORT = 9090;
private static Logger log = LoggerFactory.getLogger(CalculatorRemoteImpl.class);
public int add(int a, int b) {
List<String> addressList = lookupProviders("Calculator.add");
String address = chooseTarget(addressList);
try {
//创建Socket对象
Socket socket = new Socket(address, PORT);
OutputStream outputStream = socket.getOutputStream();//获取一个输出流,向服务端发送信息
// 将请求序列化
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
CalculateRpcRequest calculateRpcRequest = generateRequest(a, b);
// 将请求发给服务提供方
objectOutputStream.writeObject(calculateRpcRequest);
// 将响应体反序列化
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
Object response = objectInputStream.readObject();
System.out.println("response=" + response);
if (response instanceof Integer) {
return (Integer) response;
} else {
throw new InternalError();
}
} catch (Exception e) {
log.error("fail", e);
throw new InternalError();
}
}
private CalculateRpcRequest generateRequest(int a, int b) {
CalculateRpcRequest calculateRpcRequest = new CalculateRpcRequest();
calculateRpcRequest.setA(a);
calculateRpcRequest.setB(b);
calculateRpcRequest.setMethod("add");
return calculateRpcRequest;
}
private String chooseTarget(List<String> providers) {
if (null == providers || providers.size() == 0) {
throw new IllegalArgumentException();
}
return providers.get(0);
}
public static List<String> lookupProviders(String name) {
List<String> strings = new ArrayList();
strings.add("127.0.0.1");
return strings;
}
@Override
public String send(String str) {
List<String> addressList = lookupProviders("Calculator.add");
String address = chooseTarget(addressList);
try {
//创建Socket对象
Socket socket = new Socket(address, PORT);
OutputStream outputStream = socket.getOutputStream();//获取一个输出流,向服务端发送信息
// 将请求序列化
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
SendRpcRequest sendRpcRequest = generateRequest(str);
// 将请求发给服务提供方
objectOutputStream.writeObject(sendRpcRequest);
// 将响应体反序列化
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
Object response = objectInputStream.readObject();
System.out.print("response=" + response);
if (response instanceof String) {
return (String) response;
} else {
throw new InternalError();
}
} catch (Exception e) {
log.error("fail", e);
throw new InternalError();
}
}
private SendRpcRequest generateRequest(String str) {
SendRpcRequest calculateRpcRequest = new SendRpcRequest();
calculateRpcRequest.setStr(str);
calculateRpcRequest.setMethod("send");
return calculateRpcRequest;
}
}
public class ProviderApp {
private static Logger log = LoggerFactory.getLogger(ProviderApp.class);
private Calculator calculator = new CalculatorImpl();
public static void main(String[] args) throws IOException {
new ProviderApp().run();
}
private void run() throws IOException {
//创建ServerSocket对象,绑定并监听端口
ServerSocket listener = new ServerSocket(9090);
System.out.println("服务端已启动,等待客户端连接..");
try {
while (true) {
//侦听并接受到此套接字的连接,返回一个Socket对象
Socket socket = listener.accept();
try {
//得到一个输入流,接收客户端传递的信息
InputStream inputStream = socket.getInputStream();
// 将请求反序列化
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
Object object = objectInputStream.readObject();
System.out.print("object=" + object);
ObjectOutputStream objectOutputStream = null;
// 调用服务
int result = 0;
// instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例,用法为: boolean result = obj instanceof Class
if (object instanceof CalculateRpcRequest) {
CalculateRpcRequest calculateRpcRequest = (CalculateRpcRequest) object;
if ("add".equals(calculateRpcRequest.getMethod())) {
// 返回结果
result = calculator.add(calculateRpcRequest.getA(), calculateRpcRequest.getB());
objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject(new Integer(result));
} else {
throw new UnsupportedOperationException();
}
}
String result2 = null;
if (object instanceof SendRpcRequest) {
SendRpcRequest sendRpcRequest = (SendRpcRequest) object;
if ("send".equals(sendRpcRequest.getMethod())) {
// 返回结果
result2 = calculator.send(sendRpcRequest.getStr());
objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject(new String(result2));
} else {
throw new UnsupportedOperationException();
}
}
} catch (Exception e) {
log.error("fail", e);
} finally {
socket.close();
}
}
} finally {
listener.close();
}
}
}
public interface Calculator {
int add(int a, int b);
String send(String str);
}
public class CalculatorImpl implements Calculator {
@Override
public int add(int a, int b) {
int c = a + b;
System.out.print("result=" + c);
return c;
}
@Override
public String send(String str) {
System.out.print("接收到的消息" + str);
return "接收到的消息" + str;
}
}
@Data
public class SendRpcRequest implements Serializable {
private static final long serialVersionUID = 7503710091945320739L;
private String method;
private String str;
@Override
public String toString() {
return "CalculateRpcRequest{" +
"method='" + method + '\'' +
", str=" + str +
'}';
}
}
@Data
public class CalculateRpcRequest implements Serializable {
private static final long serialVersionUID = 7503710091945320739L;
private String method;
private int a;
private int b;
@Override
public String toString() {
return "CalculateRpcRequest{" +
"method='" + method + '\'' +
", a=" + a +
", b=" + b +
'}';
}
}
运行时,先启动ProviderApp
再启动ComsumerApp,可以看到打印结果
运行成功,现在我们捋一捋原理。
我们首先启动了ProviderApp,该方法通过socket 绑定并监听9090端口,
之后启动了ComsumerApp,该方法 实例化了 Calculator接口,并调用 它的两个方法add()和 send()。
Calculator接口中的两个方法在provider中的实现如下:
如果对ssm有经验,这种写法是不是很熟悉。
请注意,ComsumerApp在实例化 Calculator 时,调用的却是自己的CalculatorRemoteImpl() 方法
进入内部,可以看到这里其实是通过socket来连接provider。
其中的关键是,comsumer发送了一个 calculateRpcRequest 类给
provider。
将provider设置为断点,启动comsumer,可以看到
calculateRpcRequest的内容是方法名add,还有add所需的两个参数。
现在ProviderApp知道了方法名和参数,就调用本地的方法获取结果,并将结果返回给comsumer,过程结束。
整体而言,
两个系统通过socket连接,comsumer调用provider的add()方法和send()方法,会在自己的 CalculatorRemoteImpl实现类中,将参数序列化,通过socket发送给 server, server将参数反序列化后,再调用真正的方法进行处理,将结果序列化返回给provider。
总之,这堆demo能跑了,我们来说说这对代码的缺点。
1.provider类只提供了send和add两个方法,如果我们要调用provider的更多方法,难道每个方法都要在 CalculatorRemoteImpl 写一次socket吗?
2.我们通过socket远程连接,有没有更好的方法?netty
3.如果provider类挂了,而cosumer类还在调用,你猜旅长怎么说。
4.我们不仅要调用provider,还要providerB,providerC······多了怎么办?
5.同样,有comsumer,comsumerB,comsumerC,调用provider,顶得住吗?
我们一步一步来解决这些问题~
git 连接
https://github.com/bobly2/rpc_0.1