基于TCP的RPC简单实现
来源:互联网 发布:淘宝卖家信用度 编辑:程序博客网 时间:2024/05/21 22:46
所谓RPC就是远程方法调用(Remote Process Call ),简单的来说就是通过MQ,TCP,HTTP或者自己写的网络协议来传输我要调用对方的什么接口,对方处理之后再把结果返回给我.就这么简单的一个过程.在一个大型的项目之后基本上各模块都是分开的,以提供服务的方式进行相互调用.如果能够提供智能负载均衡,可选择的java对象编码解码协议,网络传输协议,服务监控,服务版本控制等很多功能的话就是一个SOA架构了.
前两天实现了一个基于java Socket 实现的阻塞的RPC.其原理非常简单
客户端用一个TransportMessage类去包装需要调用的接口,调用的方法,调用方法的参数类型,调用方法的参数值.
客户端用Socet连接服务端,序列化TransportMessage,传输给服务端.
服务端循环接收请求,一旦受到请求就起一个线程扔到线程池去执行,执行的内容就是反序列化TransportMessage类,在servicePool池中获取接口实现类,通过调用方法参数类型数组获取Method对象.然后通过method.invoke去调用方法.
服务器端序列化结果,然后通过socket传输给客户端.
客户端收到结果,反序列化结果对象.
具体代码实现,(为了节省篇幅,setter,getter就不放进来了):
1.远程调用信息封装 TransportMessage.java
/**
* @author Lubby
* @date 2015年4月22日 下午1:06:18
* 远程调用信息封装.
* 包括 1.调用接口名称 (包名+接口名) 2.调用方法名 3.调用参数Class类型数组 4.调用接口的参数数组
*/
public
class
TransportMessage
implements
Serializable {
//包名+接口名称 如com.lubby.rpc.service.MathService.
private
String interfaceName;
//调用方法名 如 getSum
private
String methodName;
//参数类型 按照接口参数顺序 getSum(int a, int b, String name)方法就是int.class int.class String.class的数组
private
Class[] paramsTypes;
//参数 按照接口参数顺序 getSum(int a, int b, String name)方法就是 1,3,"Tom"的数组
private
Object[] parameters;
public
TransportMessage() {
super
();
// TODO Auto-generated constructor stub
}
public
TransportMessage(String interfaceName, String methodName,
Class[] paramsTypes, Object[] parameters) {
super
();
this
.interfaceName = interfaceName;
this
.methodName = methodName;
this
.paramsTypes = paramsTypes;
this
.parameters = parameters;
}
}
2.客户端调用远程方法类 RPCClient.java
public
class
RPCClient {
// 服务端地址
private
String serverAddress;
// 服务端端口
private
int
serverPort;
// 线程池大小
private
int
threadPoolSize =
10
;
// 线程池
private
ExecutorService executorService =
null
;
public
RPCClient() {
super
();
// TODO Auto-generated constructor stub
}
/**
* @param serverAddress
* TPC服务地址
* @param serverPort
* TPC服务端口
*
*/
public
RPCClient(String serverAddress,
int
serverPort) {
this
.serverAddress = serverAddress;
this
.serverPort = serverPort;
executorService = Executors.newFixedThreadPool(threadPoolSize);
}
/**
* 同步的请求和接收结果
*
* @param transportMessage
* @return
*/
public
Object sendAndReceive(TransportMessage transportMessage) {
Object result =
null
;
Socket socket =
null
;
try
{
socket =
new
Socket(serverAddress, serverPort);
//反序列化 TransportMessage对象
ObjectOutputStream objectOutpusStream =
new
ObjectOutputStream(
socket.getOutputStream());
objectOutpusStream.writeObject(transportMessage);
ObjectInputStream objectInputStream =
new
ObjectInputStream(
socket.getInputStream());
//阻塞等待读取结果并反序列化结果对象
result = objectInputStream.readObject();
socket.close();
}
catch
(UnknownHostException e) {
e.printStackTrace();
}
catch
(IOException e) {
e.printStackTrace();
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
finally
{
try
{
//最后关闭socket
socket.close();
}
catch
(IOException e) {
e.printStackTrace();
}
}
return
result;
}
}
3.服务器处理类 RPCServer.java
public
class
RPCServer {
private
int
threadSize =
10
;
private
ExecutorService threadPool;
private
Map<String, Object> servicePool;
private
int
port =
4321
;
public
RPCServer() {
super
();
synchronized
(
this
) {
threadPool = Executors.newFixedThreadPool(
this
.threadSize);
}
}
/**
*
* @param threadSize
* 内部处理线程池大小
* @param port
* 当前TPC服务的端口号
*
*/
public
RPCServer(
int
threadSize,
int
port) {
this
.threadSize = threadSize;
this
.port = port;
synchronized
(
this
) {
threadPool = Executors.newFixedThreadPool(
this
.threadSize);
}
}
/**
*
*
* @param servicePool
* 装有service对象的Map, Key为全限定接口名,Value为接口实现类对象
* @param threadSize
* 内部处理线程池大小
* @param port
* 当前TPC服务的端口号
*
*/
public
RPCServer(Map<String, Object> servicePool,
int
threadSize,
int
port) {
this
.threadSize = threadSize;
this
.servicePool = servicePool;
this
.port = port;
synchronized
(
this
) {
threadPool = Executors.newFixedThreadPool(
this
.threadSize);
}
}
/**
* RPC服务端处理函数 监听指定TPC端口,每次有请求过来的时候调用服务,放入线程池中处理.
*
* @throws IOException
*/
public
void
service()
throws
IOException {
ServerSocket serverSocket =
new
ServerSocket(port);
while
(
true
) {
Socket receiveSocket = serverSocket.accept();
final
Socket socket = receiveSocket;
threadPool.execute(
new
Runnable() {
public
void
run() {
try
{
process(socket);
}
catch
(ClassNotFoundException e) {
e.printStackTrace();
}
catch
(NoSuchMethodException e) {
e.printStackTrace();
}
catch
(SecurityException e) {
e.printStackTrace();
}
catch
(IllegalAccessException e) {
e.printStackTrace();
}
catch
(IllegalArgumentException e) {
e.printStackTrace();
}
catch
(InvocationTargetException e) {
e.printStackTrace();
}
catch
(InstantiationException e) {
e.printStackTrace();
}
catch
(IOException e) {
e.printStackTrace();
}
finally
{
try
{
socket.close();
}
catch
(IOException e) {
e.printStackTrace();
}
}
}
});
}
}
/**
* 调用服务 通过TCP Socket返回结果对象
*
* @param receiveSocket
* 请求Socket
* @throws IOException
* @throws ClassNotFoundException
* @throws NoSuchMethodException
* @throws SecurityException
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @throws InvocationTargetException
* @throws InstantiationException
*/
private
void
process(Socket receiveSocket)
throws
IOException,
ClassNotFoundException, NoSuchMethodException, SecurityException,
IllegalAccessException, IllegalArgumentException,
InvocationTargetException, InstantiationException {
/*
* try { Thread.sleep(10000); } catch (InterruptedException e) { // TODO
* Auto-generated catch block e.printStackTrace(); }
*/
ObjectInputStream objectinputStream =
new
ObjectInputStream(
receiveSocket.getInputStream());
TransportMessage message = (TransportMessage) objectinputStream
.readObject();
// 调用服务
Object result = call(message);
ObjectOutputStream objectOutputStream =
new
ObjectOutputStream(
receiveSocket.getOutputStream());
objectOutputStream.writeObject(result);
objectinputStream.close();
objectOutputStream.close();
}
/**
* 服务处理函数 通过包名+接口名在servicePool中找到对应服务 通过调用方法参数类型数组获取Method对象
* 通过Method.invoke(对象,参数)调用对应服务
*
* @return
* @throws ClassNotFoundException
* @throws SecurityException
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InstantiationException
*/
private
Object call(TransportMessage message)
throws
ClassNotFoundException, NoSuchMethodException,
SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException,
InstantiationException {
if
(servicePool ==
null
) {
synchronized
(
this
) {
servicePool =
new
HashMap<String, Object>();
}
}
String interfaceName = message.getInterfaceName();
Object service = servicePool.get(interfaceName);
Class<?> serviceClass = Class.forName(interfaceName);
// 检查servicePool中对象,若没有着生产对象
if
(service ==
null
) {
synchronized
(
this
) {
service = serviceClass.newInstance();
servicePool.put(interfaceName, service);
}
}
Method method = serviceClass.getMethod(message.getMethodName(),
message.getParamsTypes());
Object result = method.invoke(service, message.getParameters());
return
result;
}
}
4.为了方便测试写了个接口和其实现类 MathService 和 MathServiceImpl
public
interface
MathService {
public
int
getSum(
int
a,
int
b, String name);
}
public
class
MathServiceImpl
implements
MathService {
public
int
getSum(
int
a,
int
b, String name) {
System.out.println(name);
return
a + b;
}
}
5.服务器端测试代码
public
class
ServerTest {
public
static
void
main(String[] args){
Map<String,Object> servicePool =
new
HashMap<String, Object>();
servicePool.put(
"com.lubby.rpc.service.MathService"
,
new
MathServiceImpl());
RPCServer server =
new
RPCServer(servicePool,
4
,
4321
);
try
{
server.service();
}
catch
(IOException e) {
e.printStackTrace();
}
}
}
6.客户端测试代码
public
class
ClientTest {
public
static
void
main(String[] args) {
String serverAddress =
"127.0.0.1"
;
int
serverPort =
4321
;
final
RPCClient client =
new
RPCClient(serverAddress, serverPort);
final
TransportMessage transportMessage = buildTransportMessage();
for
(
int
i =
0
; i <
1000
; i++) {
final
int
waitTime = i *
10
;
new
Thread(
new
Runnable() {
public
void
run() {
Object result = client.sendAndReceive(transportMessage);
System.out.println(result);
}
}).start();
}
}
private
static
TransportMessage buildTransportMessage() {
String interfaceName =
"com.lubby.rpc.service.MathService"
;
Class[] paramsTypes = {
int
.
class
,
int
.
class
, String.
class
};
Object[] parameters = {
1
,
3
,
"Lubby"
};
String methodName =
"getSum"
;
TransportMessage transportMessage =
new
TransportMessage(interfaceName,
methodName, paramsTypes, parameters);
return
transportMessage;
}
}
7.并发问题
由于ServerSocket是阻塞的,所以在ServerSocket.accept()方法同一时刻只能有一个线程进入,虽然之后的处理都另起一个线程,但是有瓶颈的,
我在用400个线程并发连接服务端的时候基本没问题,但是500个线程并发连接服务端的时候就会有部分线程连接不到服务器端.后面看下NIO回头用NIO来改一下.
v
http://my.oschina.net/u/2250599/blog/406474
- 基于TCP的RPC简单实现
- 基于TCP实现的最简单RPC demo
- Java实现简单的RPC调用(基于TCP协议)
- 基于TCP和HTTP协议的RPC简单实现
- 基于TCP和HTTP协议的RPC简单实现
- 基于TCP的RPC
- 基于TCP协议的RPC
- 基于TCP的RPC调用
- 系统间通信:基于TCP协议的RPC实现范例
- 系统间通信:基于TCP协议的RPC实现范例
- Scala基于Akka的Remote Actor实现的简单RPC
- 简单的RPC实现
- 简单实现的RPC
- 基于TCP协议的简单通信实现
- 开发基于TCP的RPC服务
- 基于Netty的RPC简单框架实现(一):RPC客户端
- 基于Netty的RPC简单框架实现(二):RPC服务端
- 基于protobuf的RPC实现
- Commons Collections
- 8.Java Networking: Protocol Design--协议设计
- dns
- 持续集成hudson入门
- error prj0003 error spawning 'cmd.exe' 解决方案
- 基于TCP的RPC简单实现
- HDU 1224 Free DIY Tour(DP求最短路)
- 使用 CAS 在 Tomcat 中实现单点登录
- keepalived 配置网站集群 nginx
- TCP/IP,http,socket,长连接,短连接
- js定时刷新Frame
- 四种强制类型转换的总结(const_cast、static_cast、dynamic_cast、reinterpreter_cast)
- 【学习笔记】Java抽象类和接口的一些总结
- compareTo()方法返回值 String java