分布式思想和rpc解决方案介绍
1.RPC的诞生
RPC(Remote Procedure Call)远程过程调用,通过这个rpc协议,调用远程计算机上的服务,就像调用本地的服务一样。
不同的服务部署在不同的机器上面,并且在启动后在注册中心进行注册,如果要调用,可以通过rpc调用对应的服务。
如图,在不同的Controller中可以从注册中心(可以使用eureka,zookeeper实现,本文例子使用简单的hash
map作为实现)获取可以调用的服务,然后通过rpc进行调用。
2.java远程的远程调用-RMI(Remote method Invoke)
java提供了远程的对于远程服务调用的支持:RMI(Remote method Invoke)。
3.手写一个RPC框架
3.1 实现的技术方案
设计技术点:Socket通讯、动态代理与反射、Java序列化
RPC本质是使用动态代理,通过网络通信技术进行增强。
3.2代码实现
3.2.1 客户端代码
    package main.java.rpc;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.net.InetSocketAddress;
    import java.net.Socket;
    //rpc框架的客户端代理部分
    public class RpcClientFrame {
    	   /*动态代理类,实现了对远程服务的访问*/
    	private static class DynProxy implements InvocationHandler{
    		//远程调用的服务
    		private Class serviceClass;
    		//远程调用地址
            private final InetSocketAddress addr;
    		public DynProxy(Class serviceClass,InetSocketAddress addr) {
    			this.serviceClass = serviceClass;
    			this.addr = addr;
    		}
    		@Override
    		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    			ObjectInputStream inputStream = null;
    			ObjectOutputStream outputStream = null;
    			Socket socket = null;
    			try {
    				socket = new Socket();
    				socket.connect(addr);
    				//类名 方法名 方法类型列表  方法入参列表
    				outputStream = new ObjectOutputStream(socket.getOutputStream());
    				outputStream.writeUTF(serviceClass.getSimpleName());
    				outputStream.writeUTF(method.getName());
    				outputStream.writeObject(method.getParameterTypes());
    				outputStream.writeObject(args);
    				outputStream.flush();
    				inputStream = new ObjectInputStream(socket.getInputStream());
    				 //我们要把调用的细节打印出来
                    System.out.println("远程调用成功!" + serviceClass.getName());
                    //最后要网络的请求返回给返回
                    return inputStream.readObject();
    			} catch (Exception e) {
    				e.printStackTrace();
    			} finally {
    				socket.close();
    				inputStream.close();
    				outputStream.close();
    			}
    			return null;
    		}
    	}
        //定义客户端要定义的服务
        package enjoyedu.service;
        /**
         * 享学课堂
         *类说明:服务员接口
         */
        public interface TechInterface {
            //洗脚服务
            String XJ(String name);
        }
    package main.java;
    import main.java.rpc.RpcClientFrame;
    import main.java.service.TechInterface;
    /**
     * rpc的客户端调用远程服务
     * @author hasee
     *
     */
    public class Client {
    	public static void main(String[] args) {
    	    //动态代理获取我们的对象
            TechInterface techInterface = (TechInterface) RpcClientFrame.getProxyObject(TechInterface.class);
            //进远程调用我们的对象
            System.out.println(techInterface.XJ("luke"));
    	}
    }
3.2.2服务端和注册中心代码
1.//服务端定义要调用的服务接口
package service;
public interface TechInterface {
    //洗脚服务
    String XJ(String name);
}
2.//服务端定义要调用的服务的接口实现类
package service.impl;
import service.TechInterface;
public class TechImpl implements TechInterface {
	  public String XJ(String name) {
	        return "您好,13号技师为你服务:"+name;
	    }
}
package server;
import java.io.IOException;
import javax.imageio.spi.RegisterableService;
import register.RegisterCenter;
import service.TechInterface;
import service.impl.TechImpl;
/**
 * rpc的服务端,提供服务
 * @author hasee
 *
 */
public class Server {
	public static void main(String[] args) throws IOException {
		RegisterCenter registerCenter = new RegisterCenter(8888);
        //注册技师对象至注册中心
		registerCenter.register(TechInterface.class, TechImpl.class);
		registerCenter.start();
	}
}
package register;
/**
 * 注册中心,这个例子使用一个hashmap作为实现
 * @author hasee
 *
 */
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
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;
public class RegisterCenter {
	//线程池
	private static ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
	//定义注册中心的静态对象
	private static Map<String, Class> serviceRegistry = new HashMap<String, Class>();
	//服务端口
	private static int port = 8888;
	/**
	 *  注册服务
	 * @param serviceInterface 接口名字
	 * @param impl 实现类的class对象
	 */
	public void register(Class serviceInterface, Class impl) {
		//服务的注册:socket通讯+反射
		serviceRegistry.put(serviceInterface.getSimpleName(), impl);
	}
	public RegisterCenter(int port) {
		this.port = port;
	}
	/**
	 * 启动服务端
	 * @throws IOException
	 */
	public static void start() throws IOException {
		// 创建ServerSocket实例监听端口
		ServerSocket serverSocket = new ServerSocket(port);
		System.out.println("start server");
		 // 1.监听客户端的TCP连接,接到TCP连接后将其封装成task,由线程池执行,并且同时将socket送入(server.accept()=socket)
		try {
			while (true) {
				//serverSocket.accept()会阻塞直到服务端接受到客户端的请求。
				executorService.execute(new ServiceTask(serverSocket.accept()));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 将客户端的每一个请求都封装成一个线程ServiceTask,投放到线程池里面进行执行。
	 * @author hasee
	 *
	 */
	private static class ServiceTask implements Runnable {
		private Socket client;
		public ServiceTask(Socket client) {
			this.client = client;
		}
		public void run() {
			//读取socket中的流数据
			ObjectInputStream inputStream = null;
			ObjectOutputStream outputStream = null;
			try {
				// 类名、方法名、参数类型、参数值
				inputStream = new ObjectInputStream(client.getInputStream());
				//获取调用服务名称
				String serviceName = inputStream.readUTF();
				//获取调用方法的名称
				String methodName = inputStream.readUTF();
				//获取参数类型列表
				Class<?>[] requiresTypes = (Class<?>[]) inputStream.readObject();
				//获取参数列表
				Object[] args = (Object[]) inputStream.readObject();
				Class serviceClass = serviceRegistry.get(serviceName);
				//反射调用方法
				Method method = serviceClass.getMethod(methodName, requiresTypes);
				Object result = method.invoke(serviceClass.newInstance(), args);
				//把结果反馈到客户端
				outputStream = new ObjectOutputStream(client.getOutputStream());
				outputStream.writeObject(result);
				outputStream.flush();
				//关闭io资源
				inputStream.close();
				client.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}
3.2.3 测试结果
- 先启动服务端
- 其次启动客户端
输出结果:您好,13号技师为你服务:luke
分布式思想和rpc解决方案介绍的更多相关文章
- #研发解决方案介绍#Tracing(鹰眼)
		郑昀 最后更新于2014/11/12 关键词:GoogleDapper.分布式跟踪.鹰眼.Tracing.HBase.HDFS. 本文档适用人员:研发 分布式系统为什么需要 Tracing? ... 
- 远程服务调用RPC框架介绍,微服务架构介绍和RPC框架对比,dubbo、SpringClound对比
		远程服务调用RPC框架介绍,微服务架构介绍和RPC框架对比,dubbo.SpringClound对比 远程服务调用RPC框架介绍,RPC简单的来说就是像调用本地服务一样调用远程服务. 分布式RPC需要 ... 
- [源码解析] PyTorch 分布式(18) --- 使用 RPC 的分布式管道并行
		[源码解析] PyTorch 分布式(18) --- 使用 RPC 的分布式管道并行 目录 [源码解析] PyTorch 分布式(18) --- 使用 RPC 的分布式管道并行 0x00 摘要 0x0 ... 
- Codis——分布式Redis服务的解决方案
		Codis——分布式Redis服务的解决方案 之前介绍过的 Twemproxy 是一种Redis代理,但它不支持集群的动态伸缩,而codis则支持动态的增减Redis节点:另外,官方的redis 3. ... 
- #研发解决方案介绍#基于StatsD+Graphite的智能监控解决方案
		郑昀 基于李丹和刘奎的文档 创建于2014/12/5 关键词:监控.dashboard.PHP.graphite.statsd.whisper.carbon.grafana.influxdb.Pyth ... 
- #研发解决方案介绍#基于ES的搜索+筛选+排序解决方案
		郑昀 基于胡耀华和王超的设计文档 最后更新于2014/12/3 关键词:ElasticSearch.Lucene.solr.搜索.facet.高可用.可伸缩.mongodb.SearchHub.商品中 ... 
- 【转】RPC简单介绍
		RPC简单介绍 RPC 1. RPC是什么 RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络 ... 
- Uber分布式追踪系统Jaeger使用介绍和案例
		原文:Uber分布式追踪系统Jaeger使用介绍和案例[PHP Hprose Go] 前言 随着公司的发展,业务不断增加,模块不断拆分,系统间业务调用变得越复杂,对定位线上故障带来很大困难.整个调 ... 
- 批量搞机(二):分布式ELK平台、Elasticsearch介绍、Elasticsearch集群安装、ES 插件的安装与使用
		一.分布式ELK平台 ELK的介绍: ELK 是什么? Sina.饿了么.携程.华为.美团.freewheel.畅捷通 .新浪微博.大讲台.魅族.IBM...... 这些公司都在使用 ELK!ELK! ... 
随机推荐
- swing JCheckBox 更换复选框样式
			Java Swing - 如何自定义JCheckBox复选标记图标 摘自 https://www.w3cschool.cn/java/codedemo-484050311.html import ja ... 
- C++面试笔记--字符串
			基本上求职者进行笔试没有不考字符串的.字符串也是一种相对简单的数据结构,容易被考.事实上,字符创也是一个考验程序猿编程规范和编程习惯的重要考点. 1.替换空格:实现一个函数,把字符串中的每个空格替换成 ... 
- HDU 3001 Travelling (状压DP + BFS)
			题意:有一个人要去旅游,他想要逛遍所有的城市,但是同一个城市又不想逛超过2次.现在给出城市之间的来往路费,他可以选择任意一个点为起点. 问逛遍所有城市的最低路费是多少. 析:用三进制表示每个城市的访问 ... 
- postgre-sql语法
			//客户端查询 public void pgsearchclient(HttpContext context, string starttime, string endtime, int page, ... 
- Vue 组件 生命周期函数mounted
			生命周期函数mounted:页面刷新触发mounted(){ console.log('我在页面刷新时触发');} Tips:使用export default function Add(){},与ex ... 
- 动态合并或定制GridView控件Header头某些列
			开发时,有时会对GridView控件头做一些字段合并.多行表头,多列合并,明白了其中的原理,实现起来,均能运用自如.下面Insus.NET分享自己的做法. 创建站点,创建aspx网页,拉GridVie ... 
- Insus Paging Utility Version 2
			Insus.NET对GridView或是DataList分页,都是使用自己的分页组件:http://www.cnblogs.com/insus/archive/2009/03/19/1417102.h ... 
- netty下载源码并导入idea
			netty源码导入eclipse会有一些兼容性问题,网上有解决方案,官方推荐idea,故此用idea. 拷贝git地址:https://github.com/netty/netty.git 使用git ... 
- POI2011 Tree Rotations
			POI2011 Tree Rotations 给定一个n<=2e5个叶子的二叉树,可以交换每个点的左右子树.要求前序遍历叶子的逆序对最少. 由于对于当前结点x,交换左右子树,对于范围之外的逆序对 ... 
- Flask 新闻网站
			welcome to visit http://47.94.194.236 最近在搭建django,可能内容有问题,如访问异常,请给我留言! 项目源码托管于gihub 一.项目基本流程: 1.搭 ... 
