带你手写基于 Spring 的可插拔式 RPC 框架(三)通信协议模块
在写代码之前我们先要想清楚几个问题。
- 我们的框架到底要实现什么功能?
我们要实现一个远程调用的 RPC 协议。 - 最终实现效果是什么样的?
我们能像调用本地服务一样调用远程的服务。 - 怎样实现上面的效果?
前面几章已经给大家说了,使用动态代理,在客户端生成接口代理类使用,在代理类的 invoke 方法里面将方法参数等信息组装成 request 发给服务端,服务端需要起一个服务器一直等待接收这种消息,接收之后使用反射调
用对应接口的实现类。
首先我们需要实现底层的通信的服务端和客户端,可以有一下几种实现:
基于 Socket 的客户端和服务端(同步阻塞式,不推荐),大家可以当作一个编程练习,整个和系统没有进行整合,纯粹练习使用。
基于 Socket 的服务端。
启动一个阻塞式的 socket server,加入一个线程池实现伪异步。public class SocketServer { private static SocketServer INSTANCE = new SocketServer(); private SocketServer(){}; public static SocketServer getInstance() {
return INSTANCE;
} //没有核心线程数量控制的线程池,最大线程数是 Integer 的最大值,多线程实现伪异步
ExecutorService executorService = Executors.newCachedThreadPool(); /**
* 发布服务,bio 模型
* @param service
* @param port
*/
public void publiser(int port){
try (ServerSocket serverSocket = new ServerSocket(port);)
{
while (true){
Socket socket = serverSocket.accept();//接收请求
executorService.execute(new SocketHandler(socket));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}对应的 hanlder,使用反射调用对应的服务,并通过 sokcet 写回结果。
public class SocketHandler implements Runnable{ private Socket socket; public SocketHandler(Socket socket) {
this.socket = socket;
} @Override
public void run() {
try (ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());)
{
Object o = inputStream.readObject(); //readObject 是 java 反序列化的过程
System.out.println(o);
Object result = invoke((RpcRequest) o);
//写回结果
outputStream.writeObject(result);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} private Object invoke(RpcRequest invocation){ //根据方法名和参数类型在 service 里获取方法
try {
String interFaceName = invocation.getInterfaceName();
Class impClass = Class.forName(invocation.getImpl()); Method method = impClass.getMethod(invocation.getMethodName(),invocation.getParamTypes());
String result = (String)method.invoke(impClass.newInstance(),invocation.getParams());
return result;
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} return null;
}
}
在看客户端,拼装参数,发送给 socket 服务端。
public class SocketClient { private static SocketClient INSTANCE = new SocketClient(); private SocketClient(){}; public static SocketClient getInstance() {
return INSTANCE;
} private Socket newSocket(String host, Integer port) {
System.out.println("创建一个新的 socket 连接");
try {
Socket socket = new Socket(host, port);
return socket;
} catch (IOException e) {
System.out.println("建立连接失败");
e.printStackTrace();
}
return null;
} public Object sendRequest(String host, Integer port,RpcRequest rpcRequest) {
Socket socket = newSocket(host,port);
try (
ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());)
{
outputStream.writeObject(rpcRequest);
outputStream.flush(); Object result = inputStream.readObject(); inputStream.close();
outputStream.close();
return result; } catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
通过上面的代码相信大家已经明白了这个流程了,就是一个客户端与服务端通信的过程,将需要调用的方法的参数传到服务端,服务端通过反射完成调用,最后返回结果给客户端。
下面正式开始。基于 Http 请求的客户端和基于 Tomcat 的服务端。
基于 Tomcat 的服务端,单例模式,只有一个启动服务的 start 方法,监听到的请求通过 DispatcherServlet 处理。public class HttpServer { private static HttpServer INSTANCE = new HttpServer(); private HttpServer(){} public static HttpServer getInstance(){
return INSTANCE;
} /**
*
* servlet 容器,tomcat
* @param hostname
* @param port
*/ public void start(String hostname,Integer port){ Tomcat tomcat = new Tomcat();
Server server = tomcat.getServer();
Service service = server.findService("Tomcat"); Connector connector = new Connector();
connector.setPort(port); Engine engine = new StandardEngine();
engine.setDefaultHost(hostname); Host host = new StandardHost();
host.setName(hostname); String contextPath = "";
Context context = new StandardContext();
context.setPath(contextPath);
context.addLifecycleListener(new Tomcat.FixContextListener()); //声明周期监听器 host.addChild(context);
engine.addChild(host); service.setContainer(engine);
service.addConnector(connector); tomcat.addServlet(contextPath,"dispatcher", new DispatcherServlet());
context.addServletMappingDecoded("/*","dispatcher"); try {
tomcat.start();
tomcat.getServer().await();
} catch (LifecycleException e) {
e.printStackTrace();
}
}
}下面来看请求分发器 DispatcherServlet 的实现,将请求派发给 HttpServletHandler 实现。
/**
* tomcat 是 servlet 容器,写一个 servlet
*
*/
public class DispatcherServlet extends HttpServlet { @Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
new HttpServletHandler().handler(req,resp);
}
}
HttpServletHandler 的实现其实就是解析 request,通过反射调用最后返回结果。
public class HttpServletHandler{ public void handler(HttpServletRequest req, HttpServletResponse resp) { try(InputStream inputStream = req.getInputStream();
OutputStream outputStream =resp.getOutputStream();){
ObjectInputStream ois = new ObjectInputStream(inputStream);
RpcRequest invocation = (RpcRequest) ois.readObject(); // 从注册中心根据接口,找接口的实现类
String interFaceName = invocation.getInterfaceName();
Class impClass = Class.forName(invocation.getImpl()); Method method = impClass.getMethod(invocation.getMethodName(),invocation.getParamTypes());
Object result = method.invoke(impClass.newInstance(),invocation.getParams()); RpcResponse rpcResponse = new RpcResponse();
rpcResponse.setResponseId(invocation.getRequestId());
rpcResponse.setData(result);
IOUtils.write(toByteArray(rpcResponse),outputStream);
}catch (IOException e){
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
} public byte[] toByteArray (Object obj) {
byte[] bytes = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
bytes = bos.toByteArray ();
oos.close();
bos.close();
} catch (IOException ex) {
ex.printStackTrace();
}
return bytes;
} }
最后来看客户端的实现,通过 post 方法发送数据,最后解析服务端返回的结果。
public class HttpClient { private static HttpClient INSTANCE = new HttpClient(); private HttpClient(){} public static HttpClient getInstance(){
return INSTANCE;
} public Object post(String hostname, Integer port, RpcRequest invocation){ try{
URL url = new URL("http",hostname,port,"/");
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); httpURLConnection.setRequestMethod("POST");
httpURLConnection.setDoOutput(true); OutputStream outputStream = httpURLConnection.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(outputStream);
oos.writeObject(invocation);
oos.flush();
oos.close(); InputStream inputStream = httpURLConnection.getInputStream();
RpcResponse rpcResponse = (RpcResponse)toObject(IOUtils.toByteArray(inputStream));
return rpcResponse.getData(); } catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null; } public Object toObject (byte[] bytes) {
Object obj = null;
try {
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream (bis);
obj = ois.readObject();
ois.close();
bis.close();
} catch (IOException ex) {
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
return obj;
}
}Netty 模型的客户端和服务端。
基于 Netty 的服务端,里面的编码器和解码器是我们自己实现的,大家可以先用我注释掉的那部分,等我们写到编码解码器的时候再替换。public class NettyServer { private static NettyServer INSTANCE = new NettyServer(); private static Executor executor = Executors.newCachedThreadPool(); private final static int MESSAGE_LENGTH = 4; private NettyServer(){}; public static NettyServer getInstance(){
return INSTANCE;
} private SerializeType serializeType = SerializeType.queryByType(Configuration.getInstance().getSerialize()); public static void submit(Runnable t){
executor.execute(t);
} public void start(String host, Integer port){
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(); try{
final ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>(){ @Override
protected void initChannel(SocketChannel arg0) throws Exception {
ChannelPipeline pipeline = arg0.pipeline();
//ObjectDecoder的基类半包解码器LengthFieldBasedFrameDecoder的报文格式保持兼容。因为底层的父类LengthFieldBasedFrameDecoder
//的初始化参数即为super(maxObjectSize, 0, 4, 0, 4);
// pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, NettyServer.MESSAGE_LENGTH, 0, NettyServer.MESSAGE_LENGTH));
//利用LengthFieldPrepender回填补充ObjectDecoder消息报文头
// pipeline.addLast(new LengthFieldPrepender(NettyServer.MESSAGE_LENGTH));
// pipeline.addLast(new ObjectEncoder());
//考虑到并发性能,采用weakCachingConcurrentResolver缓存策略。一般情况使用:cacheDisabled即可
// pipeline.addLast(new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
//注册解码器NettyDecoderHandler
pipeline.addLast(new NettyDecoderHandler(RpcRequest.class, serializeType));
//注册编码器NettyEncoderHandler
pipeline.addLast(new NettyEncoderHandler(serializeType));
pipeline.addLast("handler", new NettyServerHandler()); } });
Channel channel = bootstrap.bind(host, port).sync().channel();
System.out.println("Server start listen at " + port);
}catch(Exception e){
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
} }
服务端对应的 handler,netty 都是这种 handler 模式,handler 里面也是将这个接收的 request 放入线程池中处理。
public class NettyServerHandler extends SimpleChannelInboundHandler<RpcRequest> { private ChannelHandlerContext context; @Override
protected void channelRead0(ChannelHandlerContext ctx, RpcRequest rpcRequest) throws Exception {
System.out.println("server channelRead...");
System.out.println(ctx.channel().remoteAddress() + "->server:" + rpcRequest.toString());
InvokeTask it = new InvokeTask(rpcRequest,ctx);
NettyServer.submit(it);
} @Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception{
this.context = ctx;
} }
给出 InvokeTask 的对应实现。
public class InvokeTask implements Runnable{ private RpcRequest invocation;
private ChannelHandlerContext ctx; public InvokeTask(RpcRequest invocation,ChannelHandlerContext ctx) {
super();
this.invocation = invocation;
this.ctx = ctx;
} @Override
public void run() { // 从注册中心根据接口,找接口的实现类
String interFaceName = invocation.getInterfaceName();
Class impClass = null;
try {
impClass = Class.forName(invocation.getImpl());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} Method method;
Object result = null;
try {
method = impClass.getMethod(invocation.getMethodName(),invocation.getParamTypes());
//这块考虑实现类,是不是应该在 spring 里面拿
result = method.invoke(impClass.newInstance(),invocation.getParams());
} catch (Exception e) {
e.printStackTrace();
}
RpcResponse rpcResponse = new RpcResponse();
rpcResponse.setResponseId(invocation.getRequestId());
rpcResponse.setData(result);
ctx.writeAndFlush(rpcResponse).addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture channelFuture) throws Exception {
System.out.println("RPC Server Send message-id respone:" + invocation.getRequestId());
}
}); } }
再来看客户端,客户端有两种实现,一种是不能复用 handler(可以立即为 connection)的模式,这种模式并发不太高,另一种是能够复用 handler 的 handlerPool 模式。
不能复用的模式。
public class NettyClient {
private static NettyClient INSTANCE = new NettyClient(); private final static int parallel = Runtime.getRuntime().availableProcessors() * 2; private NettyClient(){}; public static NettyClient getInstance(){
return INSTANCE;
} private SerializeType serializeType = SerializeType.queryByType(Configuration.getInstance().getSerialize()); public void start(String host,Integer port){ Bootstrap bootstrap = new Bootstrap();
EventLoopGroup group = new NioEventLoopGroup(parallel); try{
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>(){ @Override
protected void initChannel(SocketChannel arg0) throws Exception {
ChannelPipeline pipeline = arg0.pipeline();
//ObjectDecoder的基类半包解码器LengthFieldBasedFrameDecoder的报文格式保持兼容。因为底层的父类LengthFieldBasedFrameDecoder
//的初始化参数即为super(maxObjectSize, 0, 4, 0, 4);
// pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
//利用LengthFieldPrepender回填补充ObjectDecoder消息报文头
// pipeline.addLast(new LengthFieldPrepender(4));
// pipeline.addLast(new ObjectEncoder());
//考虑到并发性能,采用weakCachingConcurrentResolver缓存策略。一般情况使用:cacheDisabled即可
// pipeline.addLast(new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
//注册Netty编码器
System.out.println("11111111:"+serializeType.getSerializeType());
pipeline.addLast(new NettyEncoderHandler(serializeType));
//注册Netty解码器
pipeline.addLast(new NettyDecoderHandler(RpcResponse.class, serializeType));
pipeline.addLast("handler", new NettyClientHandler()); } });
ChannelFuture future = bootstrap.connect(host,port).sync();
}catch(Exception e){
group.shutdownGracefully();
} }
}
在看可复用的模式,固定 handler 数量,目前框架中使用的是可复用模式,上面的不可复用的没用上,为了给大家理解,没有删除。
public class NettyChannelPoolFactory { //初始化Netty Channel阻塞队列的长度,该值为可配置信息
private static final int channelConnectSize = 10; //Key为服务提供者地址,value为Netty Channel阻塞队列
private static final Map<URL, ArrayBlockingQueue<Channel>> channelPoolMap = new ConcurrentHashMap<>(); private static NettyChannelPoolFactory INSTANCE = new NettyChannelPoolFactory(); private NettyChannelPoolFactory(){}; public static NettyChannelPoolFactory getInstance(){
return INSTANCE;
} private List<ServiceProvider> serviceMetaDataList = new ArrayList<>(); //根据配置文件里面需要调用的接口信息来初始化 channel
public void initNettyChannelPoolFactory(Map<String, List<ServiceProvider>> providerMap){ //将服务提供者信息存入serviceMetaDataList列表
Collection<List<ServiceProvider>> collectionServiceMetaDataList = providerMap.values();
for (List<ServiceProvider> serviceMetaDataModels : collectionServiceMetaDataList) {
if (CollectionUtils.isEmpty(serviceMetaDataModels)) {
continue;
}
serviceMetaDataList.addAll(serviceMetaDataModels);
} //获取服务提供者地址列表
Set<URL> set = new HashSet<>();
for (ServiceProvider serviceMetaData : serviceMetaDataList) {
String serviceIp = serviceMetaData.getIp();
int servicePort = serviceMetaData.getPort();
URL url = new URL(serviceIp,servicePort);
set.add(url);
} for(URL url:set){
//为每个 ip端口 建立多个 channel,并且放入阻塞队列中
int channelSize = 0;
while(channelSize < channelConnectSize){
Channel channel = null;
while(channel == null){
channel = registerChannel(url);
} channelSize ++; ArrayBlockingQueue<Channel> queue = channelPoolMap.get(url);
if(queue == null){
queue = new ArrayBlockingQueue<Channel>(channelConnectSize);
channelPoolMap.put(url, queue);
}
queue.offer(channel); }
} } public Channel registerChannel(URL url) {
final SerializeType serializeType = SerializeType.queryByType(Configuration.getInstance().getSerialize());
Bootstrap bootstrap = new Bootstrap();
EventLoopGroup group = new NioEventLoopGroup(10); try{
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>(){ @Override
protected void initChannel(SocketChannel arg0) throws Exception {
ChannelPipeline pipeline = arg0.pipeline();
//ObjectDecoder的基类半包解码器LengthFieldBasedFrameDecoder的报文格式保持兼容。因为底层的父类LengthFieldBasedFrameDecoder
//的初始化参数即为super(maxObjectSize, 0, 4, 0, 4);
// pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
//利用LengthFieldPrepender回填补充ObjectDecoder消息报文头
// pipeline.addLast(new LengthFieldPrepender(4));
// pipeline.addLast(new ObjectEncoder());
//考虑到并发性能,采用weakCachingConcurrentResolver缓存策略。一般情况使用:cacheDisabled即可
// pipeline.addLast(new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
pipeline.addLast(new NettyEncoderHandler(serializeType));
//注册Netty解码器
pipeline.addLast(new NettyDecoderHandler(RpcResponse.class, serializeType));
pipeline.addLast("handler", new NettyClientHandler()); } });
ChannelFuture future = bootstrap.connect(url.getHost(),url.getPort()).sync();
Channel channel = future.channel();
//等待Netty服务端链路建立通知信号
final CountDownLatch connectedLatch = new CountDownLatch(1); final List<Boolean> isSuccess = new ArrayList<>(1);
future.addListener(new ChannelFutureListener(){ @Override
public void operationComplete(ChannelFuture future)
throws Exception {
if(future.isSuccess()){
isSuccess.add(true);
}else{
isSuccess.add(false);
}
connectedLatch.countDown();
} });
connectedLatch.await();
if(isSuccess.get(0)){
return channel;
}
}catch(Exception e){
group.shutdownGracefully();
e.printStackTrace();
}
return null;
}
//根据 url 获取阻塞队列
public ArrayBlockingQueue<Channel> acqiure(URL url){
System.out.println(channelPoolMap.toString());
return channelPoolMap.get(url);
} //channel 使用完毕后进行回收
public void release(ArrayBlockingQueue<Channel> queue, Channel channel, URL url){
if(queue == null){
return;
}
//需要检查 channel 是否可用,如果不可用,重新注册一个放入阻塞队列中
if(channel == null || !channel.isActive() || !channel.isOpen()|| !channel.isWritable()){
if (channel != null) {
channel.deregister().syncUninterruptibly().awaitUninterruptibly();
channel.closeFuture().syncUninterruptibly().awaitUninterruptibly();
}
Channel c = null;
while(c == null){
c = registerChannel(url);
}
queue.offer(c);
return;
}
queue.offer(channel);
} }
给出对应的 handler 实现,在 channelread0 里面读取 server 端返回的信息,因为 netty 是异步的,所以需要 MessageCallBack 来实现我们的同步调用。
public class NettyClientHandler extends SimpleChannelInboundHandler<RpcResponse> { private ChannelHandlerContext context; @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
} @Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("停止时间是:"+new Date());
System.out.println("HeartBeatClientHandler channelInactive");
} @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
this.context = ctx;
System.out.println("激活时间是:"+ctx.channel().id());
} @Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, RpcResponse rpcResponse) throws Exception {
// String res = (String)msg;
//RpcResponse rpcResponse = (RpcResponse)msg;
String responseId = rpcResponse.getResponseId();
MessageCallBack callBack = ResponseHolder.getInstance().mapCallBack.get(responseId);
if(callBack != null){
ResponseHolder.getInstance().mapCallBack.remove(responseId);
callBack.over(rpcResponse);
}
}
}
MessageCallBack 的实现。
public class MessageCallBack { private RpcRequest rpcRequest; private RpcResponse rpcResponse; private Lock lock = new ReentrantLock(); private Condition finish = lock.newCondition(); public MessageCallBack(RpcRequest request) {
this.rpcRequest = request;
} public Object start() throws InterruptedException {
try {
lock.lock();
//设定一下超时时间,rpc服务器太久没有相应的话,就默认返回空吧。
finish.await(10*1000, TimeUnit.MILLISECONDS);
if (this.rpcResponse != null) {
return this.rpcResponse.getData();
} else {
return null;
}
} finally {
lock.unlock();
}
} public void over(RpcResponse reponse) {
try {
lock.lock();
this.rpcResponse = reponse;
finish.signal();
} finally {
lock.unlock();
}
} }
既然是可插拔式框架,那么底层协议一定要是可选择的,所以我们定义一个顶层接口来支持我们选择协议。
start 方法是启动服务端,send 方法是客户端发送数据。public interface Procotol { void start(URL url);
Object send(URL url, RpcRequest invocation);
}
对应的三个协议的接口实现。
Netty 的实现public class DubboProcotol implements Procotol {
@Override
public void start(URL url) {
NettyServer nettyServer = NettyServer.getInstance();
nettyServer.start(url.getHost(),url.getPort());
} @Override
public Object send(URL url, RpcRequest invocation) {
ArrayBlockingQueue<Channel> queue = NettyChannelPoolFactory.getInstance().acqiure(url);
Channel channel = null;
try {
channel = queue.poll(invocation.getTimeout(), TimeUnit.MILLISECONDS);
if(channel == null || !channel.isActive() || !channel.isOpen()|| !channel.isWritable()){
channel = queue.poll(invocation.getTimeout(), TimeUnit.MILLISECONDS);
if(channel == null){
channel = NettyChannelPoolFactory.getInstance().registerChannel(url);
}
}
//将本次调用的信息写入Netty通道,发起异步调用
ChannelFuture channelFuture = channel.writeAndFlush(invocation);
channelFuture.syncUninterruptibly();
MessageCallBack callback = new MessageCallBack(invocation);
ResponseHolder.getInstance().mapCallBack.put(invocation.getRequestId(), callback);
try {
return callback.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
} catch (InterruptedException e1) {
e1.printStackTrace();
}finally{
System.out.println("release:"+channel.id());
NettyChannelPoolFactory.getInstance().release(queue, channel, url);
}
return null;
}
}
http 的实现
public class HttpProcotol implements Procotol {
@Override
public void start(URL url) {
HttpServer httpServer = HttpServer.getInstance();
httpServer.start(url.getHost(),url.getPort());
} @Override
public Object send(URL url, RpcRequest invocation) {
HttpClient httpClient = HttpClient.getInstance();
return httpClient.post(url.getHost(),url.getPort(),invocation);
}
}
Socket 的实现
public class SocketProcotol implements Procotol {
@Override
public void start(URL url) {
SocketServer socketServer = SocketServer.getInstance();
socketServer.publiser(url.getPort());
} @Override
public Object send(URL url, RpcRequest invocation) {
SocketClient socketClient = SocketClient.getInstance();
return socketClient.sendRequest(url.getHost(),url.getPort(),invocation);
}
}
这样一个可选择协议的模型就实现了,我们可已通过这个模块选择协议,并且与服务端通信。
带你手写基于 Spring 的可插拔式 RPC 框架(三)通信协议模块的更多相关文章
- 带你手写基于 Spring 的可插拔式 RPC 框架(一)介绍
概述 首先这篇文章是要带大家来实现一个框架,听到框架大家可能会觉得非常高大上,其实这和我们平时写业务员代码没什么区别,但是框架是要给别人使用的,所以我们要换位思考,怎么才能让别人用着舒服,怎么样才能让 ...
- 带你手写基于 Spring 的可插拔式 RPC 框架(二)整体结构
前言 上一篇文章中我们已经知道了什么是 RPC 框架和为什么要做一个 RPC 框架了,这一章我们来从宏观上分析,怎么来实现一个 RPC 框架,这个框架都有那些模块以及这些模块的作用. 总体设计 在我们 ...
- 带你手写基于 Spring 的可插拔式 RPC 框架(五)注册中心
注册中心代码使用 zookeeper 实现,我们通过图片来看看我们注册中心的架构. 首先说明, zookeeper 的实现思路和代码是参考架构探险这本书上的,另外在 github 和我前面配置文件中的 ...
- 带你手写基于 Spring 的可插拔式 RPC 框架(四)代理类的注入与服务启动
上一章节我们已经实现了从客户端往服务端发送数据并且通过反射方法调用服务端的实现类最后返回给客户端的底层协议. 这一章节我们来实现客户端代理类的注入. 承接上一章,我们实现了多个底层协议,procoto ...
- C基础 带你手写 redis sds
前言 - Simple Dynamic Strings antirez 想统一 Redis,Disque,Hiredis 项目中 SDS 代码, 因此构建了这个项目 https://github.c ...
- C基础 带你手写 redis adlist 双向链表
引言 - 导航栏目 有些朋友可能对 redis 充满着数不尽的求知欲, 也许是 redis 属于工作, 交流(面试)的大头戏, 不得不 ... 而自己当下对于 redis 只是停留在会用层面, 细节层 ...
- 基于Spring MVC的Web应用开发(三) - Resources
基于Spring MVC的Web应用开发(3) - Resources 上一篇介绍了在基于Spring MVC的Web项目中加入日志,本文介绍Spring MVC如何处理资源文件. 注意到本项目的we ...
- 第二篇 基于.net搭建热插拔式web框架(沙箱的构建)
上周五写了一个实现原理篇,在评论中看到有朋友也遇到了我的问题,真的是有种他乡遇知己的感觉,整个系列我一定会坚持写完,并在最后把代码开源到git中.上一篇文章很多人看了以后,都表示不解,觉得不知道我到底 ...
- 第三篇 基于.net搭建热插拔式web框架(重造Controller)
由于.net MVC 的controller 依赖于HttpContext,而我们在上一篇中的沙箱模式已经把一次http请求转换为反射调用,并且http上下文不支持跨域,所以我们要重造一个contro ...
随机推荐
- 解决selenium和FireFox版本不兼容问题
相信很多同学刚接触selenium时,在Eclipse中打开fireFox浏览器时会报错:org.openqa.selenium.firefox.NotConnectedException: Unab ...
- angularjs 动态计算平均值
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- Codeforces Round #591 (Div. 2, based on Technocup 2020 Elimination Round 1) C. Save the Nature【枚举二分答案】
https://codeforces.com/contest/1241/problem/C You are an environmental activist at heart but the rea ...
- 【题解】P1638 逛画展-C++
原题传送门 思路这道题目可以通过尺取法来完成 (我才不管什么必须用队列)什么是尺取法呢?顾名思义,像尺子一样取一段,借用挑战书上面的话说,尺取法通常是对数组保存一对下标,即所选取的区间的左右端点,然后 ...
- ES6-21.class基本语法
1.简介(详情参考) class是构造函数的语法糖. class的constructor方法内的实现,就是原来构造函数的实现. class内的所有方法都是在prototype上的,就是原来构造函数的p ...
- Active Directory 常用属性
1.获取DirectoryEntry string str = string.Empty; string strPath = @LDAP://testDomain.com.mo; string ad ...
- Get Raster Properties获得栅格的信息
Summary Returns the properties of a raster dataset. Usage The property returned will be displayed in ...
- linux tftp配置 (Ubuntu18.04)
安装tftp客户端和服务器sudo apt-get install tftp-hpa tftpd-hpa xinetdtftp-hpa是客户端tftpd-hpa是服务器 配置文件 sudo vim / ...
- 属性 每秒10万吞吐 并发 架构 设计 58最核心的帖子中心服务IMC 类目服务 入口层是Java研发的,聚合层与检索层都是C语言研发的 电商系统里的SKU扩展服务
小结: 1. 海量异构数据的存储问题 如何将不同品类,异构的数据统一存储起来呢? (1)全品类通用属性统一存储: (2)单品类特有属性,品类类型与通用属性json来进行存储: 2. 入口层是Java研 ...
- SQL语句中有关单引号、双引号和加号的问题
字符串数据是用单引号包在外面的,而+号只是用来连接这些字符串的. 数据库里的字段是整型的时候不要加单引号,是字符串的时候要加,其它类型根据实际情况来,双引号就是用来拼接字符串的,单引号是sql文的固有 ...