实现自己的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框架(不带注册中心和带注册中心两种)的更多相关文章

  1. 手写一个RPC框架

    一.前言 前段时间看到一篇不错的文章<看了这篇你就会手写RPC框架了>,于是便来了兴趣对着实现了一遍,后面觉得还有很多优化的地方便对其进行了改进. 主要改动点如下: 除了Java序列化协议 ...

  2. 从零开始手写 dubbo rpc 框架

    rpc rpc 是基于 netty 实现的 java rpc 框架,类似于 dubbo. 主要用于个人学习,由渐入深,理解 rpc 的底层实现原理. 前言 工作至今,接触 rpc 框架已经有很长时间. ...

  3. 从0 开始手写一个 RPC 框架,轻松搞定!

    Java技术栈 www.javastack.cn 优秀的Java技术公众号 来源:juejin.im/post/5c4481a4f265da613438aec3 之前在 RPC框架底层到底什么原理得知 ...

  4. 手写开源ORM框架介绍

    手写开源ORM框架介绍 简介 前段时间利用空闲时间,参照mybatis的基本思路手写了一个ORM框架.一直没有时间去补充相应的文档,现在正好抽时间去整理下.通过思路历程和代码注释,一方面重温下知识,另 ...

  5. 手写Spring事务框架

    Spring事务基于AOP环绕通知和异常通知 编程事务 声明事务 Spring事务底层使用编程事务+AOP进行包装的   = 声明事务 AOP应用场景:  事务 权限 参数验证 什么是AOP技术 AO ...

  6. 手写spring事务框架-蚂蚁课堂

    1.视频参加C:\Users\Administrator\Desktop\蚂蚁3期\[www.zxit8.com] 0017-(每特教育&每特学院&蚂蚁课堂)-3期-源码分析-手写Sp ...

  7. 剖析手写Vue,你也可以手写一个MVVM框架

    剖析手写Vue,你也可以手写一个MVVM框架# 邮箱:563995050@qq.com github: https://github.com/xiaoqiuxiong 作者:肖秋雄(eddy) 温馨提 ...

  8. 闭关修炼180天--手写持久层框架(mybatis简易版)

    闭关修炼180天--手写持久层框架(mybatis简易版) 抛砖引玉 首先先看一段传统的JDBC编码的代码实现: //传统的JDBC实现 public static void main(String[ ...

  9. PHP生成带logo图像二维码的两种方法

    本文主要和大家分享PHP生成带logo图像二维码的两种方法,主要以文字和代码的形式和大家分享,希望能帮助到大家. 一.利用Google API生成二维码Google提供了较为完善的二维码生成接口,调用 ...

随机推荐

  1. markdown格式字串转html格式字串

    参考:https://www.jianshu.com/p/0eff6cba1b7f 操作: 1.安装python支持包 pip install mkdocs       #  含markdown包 或 ...

  2. apache 防盗链

    方法1:Apache防盗链的第一种实现方法,可以用rewrite实现 (1.)首先要确认Apache的rewrite module可用,打开 httpd.conf 文件,如果前面有注释去掉 LoadM ...

  3. cuda实现向量相加

    cuda实现向量相加 博客最后附上整体代码 如果有说的不对的地方还请前辈指出, 因为cuda真的接触没几天 一些总结(建议看) cuda 并不纯GPU在运行程序, 而是 cpu 与 gpu 一起在运行 ...

  4. chrome浏览器的跨域设置

    原文:https://www.cnblogs.com/laden666666/p/5544572.html 做前后分离的webapp开发的时候,出于一些原因往往需要将浏览器设置成支持跨域的模式,好在c ...

  5. JAVA HASH学习

    就HASH的目的来说,是为了解决内容摘要与快速索引的问题:而其算法也比较多样. JDK实现中,对String类的hashcode()进行了重载: public int hashCode() { int ...

  6. SLAM、三维重建,语义相关数据集大全

    作者朱尊杰,公众号:计算机视觉life,编辑成员 一 主要针对自动驾驶: 1.KITTI数据集: http://www.cvlibs.net/datasets/kitti/index.php(RGB+ ...

  7. springcloud注册中心Eureka<英 [juəˈri:kə]>的基本搭建

    1.http://start.spring.io搭建基本的springboot环境,版本用的是1.5.10 2.在pom文件中添加注册中心的jar包和springcloud的jar包 <!-- ...

  8. 关于Mybatis查询结果的封装

    1.结果封装为List<Object> 接口示例: public List<Members> selectMembersListByName(String name); 配置文 ...

  9. MySQL 新建用户并赋予权限

    创建一个用户: create user 'oukele'@'%' identified by 'oukele'; 提示下面所列出的信息的话,得刷新一下权限表 The MySQL server is r ...

  10. SCPI 语言简介

    电子负载中需要用到,所以记录下.来源是德科技 SCPI(可编程仪器的标准命令)是一种基于 ASCII 的仪器编程语言,供测试和测量仪器使用. SCPI 命令采用分层结构,也称为树系统. 相关命令归组于 ...