手写实现RPC框架(不带注册中心和带注册中心两种)
实现自己的RPC框架如果不需要自定义协议的话那就要基于Socket+序列化。

ProcessorHandler:
主要是用来处理客户端的请求。
package dgb.nospring.myrpc; import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket; /**
* 任务处理类
*
* @author Dongguabai
* @date 2018/11/1 16:10
*/
public class ProcessorHandler implements Runnable { private Socket socket;
/**
* 服务端发布的服务
*/
private Object service; public ProcessorHandler(Socket socket, Object service) {
this.socket = socket;
this.service = service;
} //处理请求
@Override
public void run() {
ObjectInputStream objectInputStream = null;
try {
objectInputStream = new ObjectInputStream(socket.getInputStream());
//反序列化
RpcRequest rpcRequest = (RpcRequest) objectInputStream.readObject();
Object result = invoke(rpcRequest);
//将结果返回给客户端
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject(result);
objectOutputStream.flush();
objectInputStream.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (objectInputStream != null) {
try {
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} /**
* 反射调用
*
* @param rpcRequest
*/
private Object invoke(RpcRequest rpcRequest) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
System.out.println("服务端开始调用------");
Object[] parameters = rpcRequest.getParameters();
Class[] parameterTypes = new Class[parameters.length];
for (int i = 0, length = parameters.length; i < length; i++) {
parameterTypes[i] = parameters[i].getClass();
}
Method method = service.getClass().getMethod(rpcRequest.getMethodName(), parameterTypes);
return method.invoke(service, parameters);
} }
RemoteInvocationHandler:
动态代理InvocationHandler。
package dgb.nospring.myrpc; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; /**
* @author Dongguabai
* @date 2018/11/1 16:20
*/
public class RemoteInvocationHandler implements InvocationHandler{ private String host;
private int port; /**
*发起客户端和服务端的远程调用。调用客户端的信息进行传输
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
RpcRequest rpcRequest = new RpcRequest();
rpcRequest.setClassName(method.getDeclaringClass().getName());
rpcRequest.setMethodName(method.getName());
rpcRequest.setParameters(args);
TcpTransport tcpTransport = new TcpTransport(host,port);
return tcpTransport.send(rpcRequest);
} public RemoteInvocationHandler(String host, int port) {
this.host = host;
this.port = port;
}
}
RpcClientProxy:
客户端获取代理对象。 package dgb.nospring.myrpc; import java.lang.reflect.Proxy; /**
* 客户端代理
* @author Dongguabai
* @date 2018/11/1 16:18
*/
public class RpcClientProxy { public <T> T clientProxy(final Class<T> interfaceClass,final String host,final int port){
return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),new Class[]{interfaceClass},new RemoteInvocationHandler(host, port));
}
}
RpcRequest:
封装的一个传输对象。
package dgb.nospring.myrpc; import java.io.Serializable; /**
* 统一传输对象(让服务端知道当前要做什么)
*
* @author Dongguabai
* @date 2018/11/1 16:16
*/
public class RpcRequest implements Serializable { private String className;
private String methodName;
private Object[] parameters; 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 Object[] getParameters() {
return parameters;
} public void setParameters(Object[] parameters) {
this.parameters = parameters;
}
}
RpcServer:
服务端发布服务。
package dgb.nospring.myrpc; import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
* @author Dongguabai
* @date 2018/11/1 15:53
*/
public class RpcServer {
//不建议通过Executors创建线程池,这里为了方便
private static final ExecutorService executor = Executors.newCachedThreadPool(); public void publisher(final Object service, int port) {
//启动一个服务监听
try (ServerSocket serverSocket = new ServerSocket(port)) {
while (true){
//通过ServerSocket获取请求
Socket socket = serverSocket.accept();
executor.execute(new ProcessorHandler(socket,service));
}
} catch (IOException e) {
e.printStackTrace();
}
} }
TcpTransport:
处理Socket传输。
package dgb.nospring.myrpc; import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket; /**
* socket传输
*
* @author Dongguabai
* @date 2018/11/1 16:25
*/
public class TcpTransport { private String host; private int port; public TcpTransport(String host, int port) {
this.host = host;
this.port = port;
} private Socket newSocket() {
System.out.println("准备创建Socket连接,host:" + host + ",port:" + port);
try {
Socket socket = new Socket(host, port);
return socket;
} catch (IOException e) {
throw new RuntimeException("Socket连接创建失败!host:" + host + ",port:" + port);
}
} public Object send(RpcRequest rpcRequest) {
Socket socket = null;
try {
socket = newSocket();
try {
ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(rpcRequest);
outputStream.flush();
ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
Object result = inputStream.readObject();
inputStream.close();
outputStream.close();
return result;
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("发起远程调用异常!",e);
}
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
测试Demo
接口:
package dgb.nospring.myrpc.demo; /**
* @author Dongguabai
* @date 2018/11/1 15:50
*/
public interface IHelloService { String sayHello(String name);
}
实现类: package dgb.nospring.myrpc.demo; /**
* @author Dongguabai
* @date 2018/11/1 15:51
*/
public class HelloServiceImpl implements IHelloService { @Override
public String sayHello(String name) {
return "你好," + name;
}
}
客户端:
package dgb.nospring.myrpc.demo; import dgb.nospring.myrpc.RpcClientProxy; /**
* @author Dongguabai
* @date 2018/11/1 18:10
*/
public class ClientDemo { public static void main(String[] args) {
RpcClientProxy proxy = new RpcClientProxy();
IHelloService helloService = proxy.clientProxy(IHelloService.class, "127.0.0.1", 12345);
String name = helloService.sayHello("张三");
System.out.println(name);
}
}
服务端:
package dgb.nospring.myrpc.demo; import dgb.nospring.myrpc.RpcServer; /**
* @author Dongguabai
* @date 2018/11/1 18:07
*/
public class ServerDemo { public static void main(String[] args) {
RpcServer rpcServer = new RpcServer();
rpcServer.publisher(new HelloServiceImpl(),12345);
}
}
目前大部分远程调用框架都是基于netty去实现的,毕竟Socket的性能实在不行。
作者:Dongguabai
来源:CSDN
原文:https://blog.csdn.net/Dongguabai/article/details/83624822
------------------------------------------------------------------------------------------------------------------------
基于以上完成的RPC框架进行改造,增加基于Curator实现的ZK注册中心。
项目源码地址:https://gitee.com/white_melon_white/rpcDemo
可能这个图不太准确,但是大体意思就是服务端在注册中心中注册服务,客户端在注册中心获取服务地址进行调用,中间可能还会有一些LB等:

定义一个注册服务的顶层接口IRegistryCenter:
package dgb.nospring.myrpc.registry; /**
* 注册中心顶层接口
* @author Dongguabai
* @date 2018/11/1 19:05
*/
public interface IRegistryCenter { /**
* 注册服务
* @param serviceName 服务名称
* @param serviceAddress 服务地址
*/
void register(String serviceName,String serviceAddress);
}
实现类RegistryCenterImpl:
package dgb.nospring.myrpc.registry; import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode; /**
* 注册中心实现
*
* @author Dongguabai
* @date 2018/11/1 19:10
*/
@Slf4j
public class RegistryCenterImpl implements IRegistryCenter { private CuratorFramework curatorFramework; {
curatorFramework = CuratorFrameworkFactory.builder()
.connectString(RegistryCenterConfig.CONNECTING_STR)
.sessionTimeoutMs(RegistryCenterConfig.SESSION_TIMEOUT)
.retryPolicy(new ExponentialBackoffRetry(1000, 10)).build();
curatorFramework.start();
} //注册相应服务
@Override
public void register(String serviceName, String serviceAddress) {
String serviceNodePath = RegistryCenterConfig.NAMESPACE + "/" + serviceName;
try {
//如果serviceNodePath(/rpcNode/userService)不存在就创建
if (curatorFramework.checkExists().forPath(serviceNodePath)==null){
//持久化节点
curatorFramework.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(serviceNodePath,RegistryCenterConfig.DEFAULT_VALUE);
}
//注册的服务的节点路径
String addressPath = serviceNodePath+"/"+serviceAddress;
//临时节点
String rsNode = curatorFramework.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(addressPath, RegistryCenterConfig.DEFAULT_VALUE);
log.info("服务注册成功:{}",rsNode); } catch (Exception e) {
throw new RuntimeException("注册服务出现异常!",e);
}
} }
注册中心的一些配置参数RegistryCenterConfig:
package dgb.nospring.myrpc.registry; /**
* @author Dongguabai
* @date 2018/11/1 19:13
*/
public interface RegistryCenterConfig { /**
* ZK地址int
*/
String CONNECTING_STR = "192.168.220.136,192.168.220.137"; int SESSION_TIMEOUT = 4000; /**
* 注册中心namespace
*/
String NAMESPACE = "/rpcNode"; /**
* value一般来说作用不大;一般主要是利用节点特性搞点事情
*/
byte[] DEFAULT_VALUE = "0".getBytes();
}
为了方便,增加了一个服务发布的注解RpcAnnotation,在接口的实现类上标明这个注解表示对外发布这个接口:
package dgb.nospring.myrpc.registry; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* @author Dongguabai
* @date 2018/11/2 8:54
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RpcAnnotation { /**
* 对外发布服务的接口
*
* @return
*/
Class<?> value(); /**
* 版本,用来区分不同版本
* @return
*/
String version() default "";
}
这个版本号的作用在本次Demo中没有体现出来,不过其实使用也是很简单的,可以将版本号与ZK node地址中的serviceName拼接或者绑定起来,然后根据版本号+serviceName获取相应的服务调用地址。那么客户端在发现服务的时候也要传入相应的版本进去。
首先改造服务端,服务端要将服务发布到注册中心,RpcServer:
package dgb.nospring.myrpc; import dgb.nospring.myrpc.registry.IRegistryCenter;
import dgb.nospring.myrpc.registry.RpcAnnotation;
import lombok.extern.slf4j.Slf4j; import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
* @author Dongguabai
* @date 2018/11/1 15:53
*/
@Slf4j
public class RpcServer { /**
* 注册中心
*/
private IRegistryCenter registryCenter; /**
* 服务的发布地址
*/
private String addressService; /**
* 服务名称和服务对象之间的关系
*/
private static final Map<String,Object> HANDLER_MAPPING = new HashMap<>(); //不建议通过Executors创建线程池,这里为了方便
private static final ExecutorService executor = Executors.newCachedThreadPool(); /*public void publisher(final Object service, int port) {
//启动一个服务监听
try (ServerSocket serverSocket = new ServerSocket(port)) {
while (true){
//通过ServerSocket获取请求
Socket socket = serverSocket.accept();
executor.execute(new ProcessorHandler(socket,service));
}
} catch (IOException e) {
e.printStackTrace();
}
}*/ /**
* 改造后的发布服务的方法
*/
public void publisher() {
//启动一个服务监听
//获取端口
int port = Integer.parseInt(addressService.split(":")[1]);
try (ServerSocket serverSocket = new ServerSocket(port)) {
//循环获取所有的接口Name
HANDLER_MAPPING.keySet().forEach(interfaceName->{
registryCenter.register(interfaceName,addressService);
log.info("注册服务成功:【serviceName:{},address:{}】",interfaceName,addressService);
});
while (true){
//通过ServerSocket获取请求
Socket socket = serverSocket.accept();
executor.execute(new ProcessorHandler(socket,HANDLER_MAPPING));
}
} catch (IOException e) {
e.printStackTrace();
}
} /**
* 绑定服务名称和服务对象
* @param services
*/
public void bind(Object... services){
for (Object service : services) {
//获取发布的服务接口
RpcAnnotation rpcAnnotation = service.getClass().getAnnotation(RpcAnnotation.class);
if (rpcAnnotation==null){
continue;
}
//发布接口的class
String serviceName = rpcAnnotation.value().getName();
//将serviceName和service进行绑定
HANDLER_MAPPING.put(serviceName,service);
}
} public RpcServer(IRegistryCenter registryCenter, String addressService) {
this.registryCenter = registryCenter;
this.addressService = addressService;
}
}
改造任务处理类ProcessorHandler:
package dgb.nospring.myrpc; import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.Map; /**
* 任务处理类
*
* @author Dongguabai
* @date 2018/11/1 16:10
*/
public class ProcessorHandler implements Runnable { private Socket socket;
/**
* 服务端发布的服务
*/
private Map<String,Object> handlerMap; /**
* 通过构造传入Map
* @param socket
* @param handlerMap
*/
public ProcessorHandler(Socket socket, Map<String, Object> handlerMap) {
this.socket = socket;
this.handlerMap = handlerMap;
} //处理请求
@Override
public void run() {
ObjectInputStream objectInputStream = null;
try {
objectInputStream = new ObjectInputStream(socket.getInputStream());
//反序列化
RpcRequest rpcRequest = (RpcRequest) objectInputStream.readObject();
Object result = invoke(rpcRequest);
//将结果返回给客户端
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject(result);
objectOutputStream.flush();
objectInputStream.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (objectInputStream != null) {
try {
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} /**
* 反射调用
*
* @param rpcRequest
*/
/*private Object invoke(RpcRequest rpcRequest) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
System.out.println("服务端开始调用------");
Object[] parameters = rpcRequest.getParameters();
Class[] parameterTypes = new Class[parameters.length];
for (int i = 0, length = parameters.length; i < length; i++) {
parameterTypes[i] = parameters[i].getClass();
}
Method method = service.getClass().getMethod(rpcRequest.getMethodName(), parameterTypes);
return method.invoke(service, parameters);
}*/ /**
* 反射调用(之前通过Service进行反射调用,现在通过Map获取service)
*
* @param rpcRequest
*/
private Object invoke(RpcRequest rpcRequest) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
System.out.println("服务端开始调用------");
Object[] parameters = rpcRequest.getParameters();
Class[] parameterTypes = new Class[parameters.length];
for (int i = 0, length = parameters.length; i < length; i++) {
parameterTypes[i] = parameters[i].getClass();
}
//从Map中获得Service(根据客户端请求的ServiceName,获得相应的服务),依旧是通过反射发起调用
Object service = handlerMap.get(rpcRequest.getClassName());
Method method = service.getClass().getMethod(rpcRequest.getMethodName(), parameterTypes);
return method.invoke(service, parameters);
} }
测试服务发布Demo:
package dgb.nospring.myrpc.demo; import dgb.nospring.myrpc.RpcServer;
import dgb.nospring.myrpc.registry.IRegistryCenter;
import dgb.nospring.myrpc.registry.RegistryCenterImpl; /**
* @author Dongguabai
* @date 2018/11/1 18:07
*/
public class ServerDemo { public static void main(String[] args) {
//之前发布服务
/*
RpcServer rpcServer = new RpcServer();
rpcServer.publisher(new HelloServiceImpl(),12345);
*/
//改造后
IRegistryCenter registryCenter = new RegistryCenterImpl();
//这里为了方便,获取ip地址就直接写了
RpcServer rpcServer = new RpcServer(registryCenter,"127.0.0.1:12345");
//绑定服务
rpcServer.bind(new HelloServiceImpl());
rpcServer.publisher();
}
}
运行结果:

在ZK客户端:

服务客户发布后,现在要解决的就是服务发现的问题。
定义一个顶层服务发现接口IServiceDiscovery:
package dgb.nospring.myrpc.registry; /**
* @author Dongguabai
* @date 2018/11/2 9:55
*/
public interface IServiceDiscovery { /**
* 根据接口名称发现服务调用地址
* @param serviceName
* @return
*/
String discover(String serviceName);
}
实现类:
package dgb.nospring.myrpc.registry; import dgb.nospring.myrpc.registry.loadbalance.LoadBalance;
import dgb.nospring.myrpc.registry.loadbalance.RandomLoadBanalce;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry; import java.util.List; /**
* 服务发现实现类
* @author Dongguabai
* @date 2018/11/2 9:56
*/
@Slf4j
public class ServiceDiscoveryImpl implements IServiceDiscovery { /**
* /rpcNode/dgb.nospring.myrpc.demo.IHelloService
* 当前服务下所有的协议地址
*/
private List<String> repos; /**
* ZK地址
*/
private String zkAddress; private CuratorFramework curatorFramework; @Override
public String discover(String serviceName) {
//获取/rpcNode/dgb.nospring.myrpc.demo.IHelloService下所有协议地址
String nodePath = RegistryCenterConfig.NAMESPACE+"/"+serviceName;
try {
repos = curatorFramework.getChildren().forPath(nodePath);
} catch (Exception e) {
throw new RuntimeException("服务发现获取子节点异常!",e);
}
//动态发现服务节点变化,需要注册监听
registerWatcher(nodePath); //这里为了方便,直接使用随机负载
LoadBalance loadBalance = new RandomLoadBanalce();
return loadBalance.selectHost(repos);
} /**
* 监听节点变化,给repos重新赋值
* @param path
*/
private void registerWatcher(String path){
PathChildrenCache pathChildrenCache = new PathChildrenCache(curatorFramework,path,true);
PathChildrenCacheListener pathChildrenCacheListener = new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
repos = curatorFramework.getChildren().forPath(path);
}
};
pathChildrenCache.getListenable().addListener(pathChildrenCacheListener);
try {
pathChildrenCache.start();
} catch (Exception e) {
throw new RuntimeException("监听节点变化异常!",e);
}
} public ServiceDiscoveryImpl(String zkAddress) {
this.zkAddress = zkAddress;
curatorFramework = CuratorFrameworkFactory.builder()
.connectString(RegistryCenterConfig.CONNECTING_STR)
.sessionTimeoutMs(RegistryCenterConfig.SESSION_TIMEOUT)
.retryPolicy(new ExponentialBackoffRetry(1000, 10)).build();
curatorFramework.start();
}
}
还有一套负载算法(这里简单实现了一个随机负载):
package dgb.nospring.myrpc.registry.loadbalance; import java.util.List; /**
* 负载顶层接口
* @author Dongguabai
* @date 2018/11/2 10:11
*/
public interface LoadBalance { String selectHost(List<String> repos);
}
package dgb.nospring.myrpc.registry.loadbalance; import org.apache.commons.collections.CollectionUtils; import java.util.List; /**
* @author Dongguabai
* @date 2018/11/2 10:15
*/
public abstract class AbstractLoadBanance implements LoadBalance{ /**
* 通过模板方法,做一些牵制操作
* @param repos
* @return
*/
@Override
public String selectHost(List<String> repos) {
if(CollectionUtils.isEmpty(repos)){
return null;
}
if(repos.size()==1){
return repos.get(0);
}
return doSelect(repos);
} /**
* 实现具体的实现负载算法
* @param repos
* @return
*/
protected abstract String doSelect(List<String> repos); }
package dgb.nospring.myrpc.registry.loadbalance; import java.util.List;
import java.util.Random; /**
* 随机负载算法
* @author Dongguabai
* @date 2018/11/2 10:17
*/
public class RandomLoadBanalce extends AbstractLoadBanance{ @Override
protected String doSelect(List<String> repos) {
return repos.get(new Random().nextInt(repos.size()));
}
} 还有获取服务的RpcClientProxy需要进行改造,其实就是改了一个参数传递而已: package dgb.nospring.myrpc; import dgb.nospring.myrpc.registry.IServiceDiscovery; import java.lang.reflect.Proxy; /**
* 客户端代理
* @author Dongguabai
* @date 2018/11/1 16:18
*/
public class RpcClientProxy { private IServiceDiscovery serviceDiscovery; /* public <T> T clientProxy(final Class<T> interfaceClass,final String host,final int port){
return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),new Class[]{interfaceClass},new RemoteInvocationHandler(host, port));
}*/
public <T> T clientProxy(final Class<T> interfaceClass){
return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),new Class[]{interfaceClass},new RemoteInvocationHandler(serviceDiscovery));
} public RpcClientProxy(IServiceDiscovery serviceDiscovery) {
this.serviceDiscovery = serviceDiscovery;
}
}
同样的,动态代理的InvocationHandler也要修改:
package dgb.nospring.myrpc; import dgb.nospring.myrpc.registry.IServiceDiscovery; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; /**
* @author Dongguabai
* @date 2018/11/1 16:20
*/
public class RemoteInvocationHandler implements InvocationHandler{ private IServiceDiscovery serviceDiscovery; /**
*发起客户端和服务端的远程调用。调用客户端的信息进行传输
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
RpcRequest rpcRequest = new RpcRequest();
rpcRequest.setClassName(method.getDeclaringClass().getName());
rpcRequest.setMethodName(method.getName());
rpcRequest.setParameters(args);
//从ZK中获取地址 127.0.0.1:12345
String discover = serviceDiscovery.discover(rpcRequest.getClassName());
TcpTransport tcpTransport = new TcpTransport(discover);
return tcpTransport.send(rpcRequest);
} public RemoteInvocationHandler(IServiceDiscovery serviceDiscovery) {
this.serviceDiscovery = serviceDiscovery;
}
}
同样的。TCPTransport也要进行改造:
package dgb.nospring.myrpc; import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket; /**
* socket传输
*
* @author Dongguabai
* @date 2018/11/1 16:25
*/
public class TcpTransport { private String serviceAddress; private Socket newSocket() {
System.out.println("准备创建Socket连接,"+serviceAddress);
String[] split = serviceAddress.split(":");
try {
Socket socket = new Socket(split[0], Integer.parseInt(split[1]));
return socket;
} catch (IOException e) {
throw new RuntimeException("Socket连接创建失败!" + serviceAddress);
}
} public TcpTransport(String serviceAddress) {
this.serviceAddress = serviceAddress;
} public Object send(RpcRequest rpcRequest) {
Socket socket = null;
try {
socket = newSocket();
try {
ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(rpcRequest);
outputStream.flush();
ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
Object result = inputStream.readObject();
inputStream.close();
outputStream.close();
return result;
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("发起远程调用异常!",e);
}
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
客户端Demo:
package dgb.nospring.myrpc.demo; import dgb.nospring.myrpc.RpcClientProxy;
import dgb.nospring.myrpc.registry.IServiceDiscovery;
import dgb.nospring.myrpc.registry.RegistryCenterConfig;
import dgb.nospring.myrpc.registry.ServiceDiscoveryImpl; /**
* @author Dongguabai
* @date 2018/11/1 18:10
*/
public class ClientDemo { public static void main(String[] args) {
/*RpcClientProxy proxy = new RpcClientProxy();
IHelloService helloService = proxy.clientProxy(IHelloService.class, "127.0.0.1", 12345);
String name = helloService.sayHello("张三");
System.out.println(name);*/ IServiceDiscovery serviceDiscovery = new ServiceDiscoveryImpl(RegistryCenterConfig.CONNECTING_STR);
RpcClientProxy proxy = new RpcClientProxy(serviceDiscovery);
IHelloService service = proxy.clientProxy(IHelloService.class);
System.out.println(service.sayHello("张三")); }
}
控制台输出:

如果需要验证集群环境下,我们可以创建两个ServerDemo:

两个服务均注册到注册中心:


客户端调用还是不变:

连续调用两次客户端:


---------------------
作者:Dongguabai
来源:CSDN
原文:https://blog.csdn.net/dongguabai/article/details/83625362
手写实现RPC框架(不带注册中心和带注册中心两种)的更多相关文章
- 手写一个RPC框架
一.前言 前段时间看到一篇不错的文章<看了这篇你就会手写RPC框架了>,于是便来了兴趣对着实现了一遍,后面觉得还有很多优化的地方便对其进行了改进. 主要改动点如下: 除了Java序列化协议 ...
- 从零开始手写 dubbo rpc 框架
rpc rpc 是基于 netty 实现的 java rpc 框架,类似于 dubbo. 主要用于个人学习,由渐入深,理解 rpc 的底层实现原理. 前言 工作至今,接触 rpc 框架已经有很长时间. ...
- 从0 开始手写一个 RPC 框架,轻松搞定!
Java技术栈 www.javastack.cn 优秀的Java技术公众号 来源:juejin.im/post/5c4481a4f265da613438aec3 之前在 RPC框架底层到底什么原理得知 ...
- 手写开源ORM框架介绍
手写开源ORM框架介绍 简介 前段时间利用空闲时间,参照mybatis的基本思路手写了一个ORM框架.一直没有时间去补充相应的文档,现在正好抽时间去整理下.通过思路历程和代码注释,一方面重温下知识,另 ...
- 手写Spring事务框架
Spring事务基于AOP环绕通知和异常通知 编程事务 声明事务 Spring事务底层使用编程事务+AOP进行包装的 = 声明事务 AOP应用场景: 事务 权限 参数验证 什么是AOP技术 AO ...
- 手写spring事务框架-蚂蚁课堂
1.视频参加C:\Users\Administrator\Desktop\蚂蚁3期\[www.zxit8.com] 0017-(每特教育&每特学院&蚂蚁课堂)-3期-源码分析-手写Sp ...
- 剖析手写Vue,你也可以手写一个MVVM框架
剖析手写Vue,你也可以手写一个MVVM框架# 邮箱:563995050@qq.com github: https://github.com/xiaoqiuxiong 作者:肖秋雄(eddy) 温馨提 ...
- 闭关修炼180天--手写持久层框架(mybatis简易版)
闭关修炼180天--手写持久层框架(mybatis简易版) 抛砖引玉 首先先看一段传统的JDBC编码的代码实现: //传统的JDBC实现 public static void main(String[ ...
- PHP生成带logo图像二维码的两种方法
本文主要和大家分享PHP生成带logo图像二维码的两种方法,主要以文字和代码的形式和大家分享,希望能帮助到大家. 一.利用Google API生成二维码Google提供了较为完善的二维码生成接口,调用 ...
随机推荐
- Linux命令——killall 、kill 、pkill、xkill
参考:killall .kill .pkill 命令详解 Using kill, killall, and pkill 4 Ways to Kill a Process – kill, killall ...
- CISCO设备配置SSH 登陆
1. 设置设备域名CISCO ip domain name CISCO2.创建密钥Server(config)#crypto key generate rsa The name for the key ...
- 子div撑不开父div的几种解决办法:
如何修正DIV float之后导致的外部容器不能撑开的问题 在写HTML代码的时候,发现在Firefox等符合W3C标准的浏览器中,如果有一个DIV作为外部容器,内部的DIV如果设置了float样 ...
- 2019招商银行M-Geeker线上比赛题解析
目录 1. 最大子序和(变体) 2. 矩阵求乘积最大 3. 逐渐平均--值最大 目前已更新:第一题,第二题,第四题 1. 最大子序和(变体) 题目描述: 首先考虑常规的最大子序和的问题,即不能去掉中间 ...
- Dos命令获取当前时间
= = 这个真的折腾死我了.... 参考:http://bbs.bathome.net/thread-3328-1-1.html 操作系统不同,日期格式也可能不同: 星期二 2008-07-29 20 ...
- Jmeter+Jenkins持续集成(三、集成到Jenkins)
1.Jenkins全局工具配置 登录jenkins->系统管理->Global Tool Configuration (1)JDK配置 (2)Ant配置 配置信息按照机器上实际安装的来填写 ...
- Java锁--CountDownLatch
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3533887.html CountDownLatch简介 CountDownLatch是一个同步辅助类,在 ...
- java中的finally用法总结
不管 try 语句块正常结束还是异常结束,finally 语句块是保证要执行的.如果 try 语句块正常结束,那么在 try 语句块中的语句都执行完之后,再执行 finally 语句块.如果 try ...
- python 虚拟环境相关命令
1.总是记不住一些关于创建虚拟环境得命令,特在自己得博客里记录一下自己常用得命令: virtualenv -p C:\Python36\python D:\virtual\Envs\AssetScan ...
- 原生js手机端触摸下拉刷新
废话不多说,直接上代码,这里感谢我的好朋友,豆姐 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ...