package com.cgs.nettytest;
import java.net.InetSocketAddress;
import com.cgs.nio.RpcRequest;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
public class EchoClient {
private String host;
private int port;
private static EventLoopGroup group = new NioEventLoopGroup();
private static Bootstrap b = new Bootstrap();
private RpcRequest rpcRequest;
public EchoClient(String host,int port){
this.host = host;
this.port = port;
}
public EchoClient(){
init();
}
public void init(){
this.host = "127.0.0.1";
this.port = 20000;
}
public void setRpcRequest(RpcRequest rpcRequest){
this.rpcRequest = rpcRequest;
}
public void start() throws Exception{
b.group(group).channel(NioSocketChannel.class).remoteAddress(new InetSocketAddress(this.host,this.port)).handler(new ChannelInitializer(){
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// TODO Auto-generated method stub
System.out.println("connected..");
ch.pipeline().addLast(new EchoClientHandler(rpcRequest));
}
});
ChannelFuture cf = b.connect().sync();
System.out.println("connected compelete");
}
public void close() throws Exception{
System.out.println("connected compelete");
System.out.println("closed..");
group.shutdownGracefully().sync(); // 释放线程池资源
}
public static void main(String[] args) {
try {
new EchoClient("127.0.0.1", 20000).start();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // 连接127.0.0.1/65535,并启动
}
}
(){
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// TODO Auto-generated method stub
System.out.println("connected..");
ch.pipeline().addLast(new EchoClientHandler(rpcRequest));
}
});
ChannelFuture cf = b.connect().sync();
System.out.println("connected compelete");
}
public void close() throws Exception{
System.out.println("connected compelete");
System.out.println("closed..");
group.shutdownGracefully().sync(); // 释放线程池资源
}
public static void main(String[] args) {
try {
new EchoClient("127.0.0.1", 20000).start();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // 连接127.0.0.1/65535,并启动
}
}
远程过程调用协议,基于C/S模型,其主要核心就是调用远程方法就和调用本地方法形式几乎一样,rpc框架隐藏了底层的数据传输的细节,能够让人更加方便的写出分布式的程序。下图展现了一个典型的rpc框架的运行过程:
以上是一个基于java平台的rpc框架的调用过程。下面笔者用开源的netty网络库实现一个rpc框架,技术背景:netty,cglib。
实现的关键点: 1.动态代理。2.消息的封装。
首先给客户端调用的代码写一个动态代理:package com.cgs.nio;
import java.lang.reflect.Method;
import com.cgs.nettytest.EchoClient;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class RpcCglibClientProxy implements MethodInterceptor{
private static RpcCglibClientProxy proxy = new RpcCglibClientProxy();
private static EchoClient echoClient = new EchoClient();
private Object target;
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// TODO Auto-generated method stub
RpcRequest rpcRequest = new RpcRequest();
String className = object.getClass().getName();
rpcRequest.setRequestId(String.valueOf(System.currentTimeMillis()));
rpcRequest.setMethodName(method.getName());
rpcRequest.setParameters(args);
rpcRequest.setParameterTypes(method.getParameterTypes());
rpcRequest.setClassName(object.getClass().getName());
echoClient.setRpcRequest(rpcRequest);
echoClient.start();
echoClient.close();;
return null;
}
public Object createProxy(Class classzz){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(classzz);
enhancer.setCallback(this);
return enhancer.create();
}
public static RpcCglibClientProxy getInstance(){
return proxy;
}
public static void main(String[] args) {
TestInterface object = (TestInterface) RpcCglibClientProxy.getInstance().createProxy(TestInterface.class);
object.testMethod("test");
}
}
动态代理的功能是将为客户端调用的接口额外生成一个代理,用来替换掉其原本的接口方法,在代理的方法中,新建一个rpc客户端,并且获取方法的名称和参数,以及参数的类型,将其封装为一个请求消息的实体。通过rpc的客户端将其序列化之后,通过客户端向服务器端发送消息。消息实体的代码如下
该实体主要是封装了以下的几个属性:1.消息id.2调用的接口的类名。3.调用的方法名.4.调用的参数值,调用的参数类型。消息实体主要是为了服务端的反射提供数据类型。
epackage com.cgs.nio;
import java.io.Serializable;
public class RpcRequest implements Serializable{
/**
*
*/
private static final long serialVersionUID = 5186013065336159778L;
private String requestId;
private String className;
private String methodName;
private Class[] parameterTypes;
private Object[] parameters;
public String getRequestId() {
return requestId;
}
public void setRequestId(String requestId) {
this.requestId = requestId;
}
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[] getParameterTypes() {
return parameterTypes;
}
public void setParameterTypes(Class[] classes) {
this.parameterTypes = classes;
}
public Object[] getParameters() {
return parameters;
}
public void setParameters(Object[] parameters) {
this.parameters = parameters;
}
}
package com.cgs.nettytest;
import java.net.InetSocketAddress;
import com.cgs.nio.RpcRequest;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
public class EchoClient {
private String host;
private int port;
private static EventLoopGroup group = new NioEventLoopGroup();
private static Bootstrap b = new Bootstrap();
private RpcRequest rpcRequest;
public EchoClient(String host,int port){
this.host = host;
this.port = port;
}
public EchoClient(){
init();
}
public void init(){
this.host = "127.0.0.1";
this.port = 20000;
}
public void setRpcRequest(RpcRequest rpcRequest){
this.rpcRequest = rpcRequest;
}
public void start() throws Exception{
b.group(group).channel(NioSocketChannel.class).remoteAddress(new InetSocketAddress(this.host,this.port)).handler(new ChannelInitializer(){
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// TODO Auto-generated method stub
System.out.println("connected..");
ch.pipeline().addLast(new EchoClientHandler(rpcRequest));
}
});
ChannelFuture cf = b.connect().sync();
System.out.println("connected compelete");
}
public void close() throws Exception{
System.out.println("connected compelete");
System.out.println("closed..");
group.shutdownGracefully().sync(); // 释放线程池资源
}
public static void main(String[] args) {
try {
new EchoClient("127.0.0.1", 20000).start();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // 连接127.0.0.1/65535,并启动
}
}
[] parameterTypes;
private Object[] parameters;
public String getRequestId() {
return requestId;
}
public void setRequestId(String requestId) {
this.requestId = requestId;
}
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[] getParameterTypes() {
return parameterTypes;
}
public void setParameterTypes(Class[] classes) {
this.parameterTypes = classes;
}
public Object[] getParameters() {
return parameters;
}
public void setParameters(Object[] parameters) {
this.parameters = parameters;
}
}
package com.cgs.nettytest;
import java.net.InetSocketAddress;
import com.cgs.nio.RpcRequest;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
public class EchoClient {
private String host;
private int port;
private static EventLoopGroup group = new NioEventLoopGroup();
private static Bootstrap b = new Bootstrap();
private RpcRequest rpcRequest;
public EchoClient(String host,int port){
this.host = host;
this.port = port;
}
public EchoClient(){
init();
}
public void init(){
this.host = "127.0.0.1";
this.port = 20000;
}
public void setRpcRequest(RpcRequest rpcRequest){
this.rpcRequest = rpcRequest;
}
public void start() throws Exception{
b.group(group).channel(NioSocketChannel.class).remoteAddress(new InetSocketAddress(this.host,this.port)).handler(new ChannelInitializer(){
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// TODO Auto-generated method stub
System.out.println("connected..");
ch.pipeline().addLast(new EchoClientHandler(rpcRequest));
}
});
ChannelFuture cf = b.connect().sync();
System.out.println("connected compelete");
}
public void close() throws Exception{
System.out.println("connected compelete");
System.out.println("closed..");
group.shutdownGracefully().sync(); // 释放线程池资源
}
public static void main(String[] args) {
try {
new EchoClient("127.0.0.1", 20000).start();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // 连接127.0.0.1/65535,并启动
}
}
package com.cgs.io;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import com.cgs.config.Config;
public class JarUtil {
private static List classzz = new ArrayList();
private static JarFile jarFile = null;
private static String staticPath = "file:f:/test.jar";
private static ConcurrentHashMap hashMap = new ConcurrentHashMap();
static{
// try {
// loadFilePath(Config.getLoadJarPath());
// } catch (Exception e){
// throw new RuntimeException(e);
// }
}
public static void loadFilePath(String filepath) throws IOException, ClassNotFoundException{
jarFile = new JarFile(filepath);
List jarEntryList = new ArrayList();
Enumeration ee = jarFile.entries();
while(ee.hasMoreElements()){
JarEntry entry = (JarEntry)ee.nextElement();
if(entry.getName().contains(".class") && !entry.getName().contains("classpath")){
jarEntryList.add(entry);
}
}
URL url = new URL(staticPath);
URL[] urls = new URL[]{url};
URLClassLoader loader = new URLClassLoader(urls);
for(JarEntry entry : jarEntryList){
String className = entry.getName().replace('/', '.');
className = className.substring(0, className.length() - 6);
Class c = loader.loadClass(className);
if(!c.isInterface()){
hashMap.put(className, c);
}
}
loader.close();
}
public void loadClassFile(File[] jarFiles){
if(jarFiles != null){
try {
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
boolean accessible = method.isAccessible();
if(accessible){
method.setAccessible(true);
}
URLClassLoader classLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
} catch (Exception e) {
}
}
}
public static ConcurrentHashMap getHashMap() {
return hashMap;
}
public static void main(String[] args) {
String filePath = "F:/test.jar";
try {
JarUtil.loadFilePath(filePath);
} catch (Exception e) {
e.printStackTrace();
}
}
}
classzz = new ArrayList();
private static JarFile jarFile = null;
private static String staticPath = "file:f:/test.jar";
private static ConcurrentHashMap hashMap = new ConcurrentHashMap();
static{
// try {
// loadFilePath(Config.getLoadJarPath());
// } catch (Exception e){
// throw new RuntimeException(e);
// }
}
public static void loadFilePath(String filepath) throws IOException, ClassNotFoundException{
jarFile = new JarFile(filepath);
List jarEntryList = new ArrayList();
Enumeration ee = jarFile.entries();
while(ee.hasMoreElements()){
JarEntry entry = (JarEntry)ee.nextElement();
if(entry.getName().contains(".class") && !entry.getName().contains("classpath")){
jarEntryList.add(entry);
}
}
URL url = new URL(staticPath);
URL[] urls = new URL[]{url};
URLClassLoader loader = new URLClassLoader(urls);
for(JarEntry entry : jarEntryList){
String className = entry.getName().replace('/', '.');
className = className.substring(0, className.length() - 6);
Class c = loader.loadClass(className);
if(!c.isInterface()){
hashMap.put(className, c);
}
}
loader.close();
}
public void loadClassFile(File[] jarFiles){
if(jarFiles != null){
try {
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
boolean accessible = method.isAccessible();
if(accessible){
method.setAccessible(true);
}
URLClassLoader classLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
} catch (Exception e) {
}
}
}
public static ConcurrentHashMap getHashMap() {
return hashMap;
}
public static void main(String[] args) {
String filePath = "F:/test.jar";
try {
JarUtil.loadFilePath(filePath);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.cgs.nio;
import java.io.Serializable;
public class RpcResponse implements Serializable{
private static final long serialVersionUID = 1L;
private String requestId;
private Throwable error;
private Object result;
public String getRequestId() {
return requestId;
}
public void setRequestId(String requestId) {
this.requestId = requestId;
}
public Throwable getError() {
return error;
}
public void setError(Throwable error) {
this.error = error;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
}
package com.cgs.nettytest;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class EchoServer {
private final int port;
public EchoServer(int port){
this.port = port;
}
public void start() throws Exception{
EventLoopGroup group = new NioEventLoopGroup();
try{
ServerBootstrap sb = new ServerBootstrap();
sb.group(group).channel(NioServerSocketChannel.class).localAddress(this.port).childHandler(new ChannelInitializer(){
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// TODO Auto-generated method stub
System.out.println("connect... Client: " + ch.remoteAddress());
ch.pipeline().addLast(new EchoServerHandler());
}
});
ChannelFuture cf = sb.bind().sync();
cf.channel().closeFuture().sync();
}finally{
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) {
try {
new EchoServer(20000).start();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
(){
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// TODO Auto-generated method stub
System.out.println("connect... Client: " + ch.remoteAddress());
ch.pipeline().addLast(new EchoServerHandler());
}
});
ChannelFuture cf = sb.bind().sync();
cf.channel().closeFuture().sync();
}finally{
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) {
try {
new EchoServer(20000).start();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}