分布式思想和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! ...
随机推荐
- javascript 准确的判断类型方法
在 JavaScript 里使用 typeof 来判断数据类型,只能区分基本类型,即 “number”,”string”,”undefined”,”boolean”,”object” 五种. 对于数组 ...
- SDUT 3341 数据结构实验之二叉树二:遍历二叉树
数据结构实验之二叉树二:遍历二叉树 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 已知二叉 ...
- Thread.currentThread().getContextClassLoader() 和 Class.getClassLoader()区别
查了一些资料也不是太明白两个的区别,但是前者是最安全的用法 打个简单的比方,你一个WEB程序,发布到Tomcat里面运行.首先是执行Tomcat org.apache.catalina.startup ...
- 选择性搜索(Selective Search)
1 概述 本文牵涉的概念是候选区域(Region Proposal ),用于物体检测算法的输入.无论是机器学习算法还是深度学习算法,候选区域都有用武之地. 2 物体检测和物体识别 物体识别是要分辨出图 ...
- Django会话,用户和注册之session
鉴于cookie的不安全,django自带的session框架会帮我们搞定这些问题 你可以用session 框架来存取每个访问者任意数据, 这些数据在服务器端存储,并对cookie的收发进行了抽象. ...
- thinkphp 连接数据库 & 实例化模型操作 (下接thinkphp CURD 操作)/慕课
7.1 连接数据库 (06:15) 1 7.2实例化模型 1 1.实例化基础模型 2 2. 实例化用户自定义模型 2 问题 2 3. 实例化公共模型 4 4. 实例化空模型 7 7.1 连接数据 ...
- P1919 【模板】A*B Problem升级版(FFT快速傅里叶)
题目描述 给出两个n位10进制整数x和y,你需要计算x*y. 输入输出格式 输入格式: 第一行一个正整数n. 第二行描述一个位数为n的正整数x. 第三行描述一个位数为n的正整数y. 输出格式: 输出一 ...
- 线程池之ThreadPoolExecutor详解
为什么要使用线程池 线程是一个操作系统概念.操作系统负责这个线程的创建.挂起.运行.阻塞和终结操作.而操作系统创建线程.切换线程状态.终结线程都要进行CPU调度——这是一个耗费时间和系统资源的事情. ...
- memcache (持续了解ing...)
mem cache 英[kæʃ] 美[kæʃ]vt. 贮藏; memcache是一套分布式的高速缓存系统,目前被许多网站使用以提升网站的访问速度,尤其对于一些大型的.需要频繁访问数据库的网站访问速度提 ...
- P2446 [SDOI2010]大陆争霸
\(\color{#0066ff}{ 题目描述 }\) 幻想历8012年5月12日深夜,斯普林·布拉泽降下神谕:"Trust me, earn eternal life."克里斯军 ...