转自:http://www.cnblogs.com/rjzheng/p/8971629.html#3977269
担心后面忘了,先转了,后面借鉴实现一下RPC
--------------------------------------------------------------------------------------------------------------------------------------

引言

本文利用java自带的socket编程实现了一个简单的rpc调用框架,由两个工程组成分别名为battercake-provider(服务提供者)、battercake-consumer(服务调用者)。

设计思路如下:
1、在battercake-provider中,写一个服务叫BatterCakeService

2、在battercake-provider中,启动RpcProvider,发布该服务

3、在battercake-consumer中,启动测试类RpcTest

4、在battercake-consumer中,利用jdk动态代理,获得BatterCakeService的动态代理类BatterCakeService$Proxy0

5、在battercake-consumer中,动态代理类BatterCakeService$Proxy0,与battercake-provider建立socket连接,battercake-provider针对每一个连接,都会启动一个ServerThread处理请求,代理类则发送服务参数等相关信息

6、在battercake-consumer中,接收battercake-provider的ServerThread请求返回的结果。

上述过程时序图如下所示

接下来上代码!!

服务提供者

本部分的工程为battercake-provider,项目结构图如下图所示


先上使用的部分的代码
先创建一个微服务,接口如下

package com.rjzheng.service;

public interface BatterCakeService {
/**
* 卖煎饼的服务
* @param name
* @return
*/
public String sellBatterCake(String name);
}

实现类如下

package com.rjzheng.service.impl;

import com.rjzheng.service.BatterCakeService;

public class BatterCakeServiceImpl implements BatterCakeService {

    @Override
public String sellBatterCake(String name) {
// TODO Auto-generated method stub
return name+"煎饼,卖的特别好";
} }

接下来就是发布服务

package com.rjzheng.start;

import com.rjzheng.rpc.RpcProvider;
import com.rjzheng.service.BatterCakeService;
import com.rjzheng.service.impl.BatterCakeServiceImpl; public class RpcBootStrap {
public static void main(String[] args) throws Exception {
BatterCakeService batterCakeService =new BatterCakeServiceImpl();
//发布卖煎饼的服务,注册在20006端口
RpcProvider.export(20006,batterCakeService);
}
}

接下来是rpc框架调用部分的代码,RpcProvider,该部分代码可以总结为两步

  1. 将需要发布的服务存储在一个内存变量serviceList中
  2. 启动socket,server.accept()方法阻塞在那,监听输入
  3. 针对每一个请求,单独启动一个线程处理
package com.rjzheng.rpc;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* RPC服务提供器
* @author zhengrongjun
*
*/
public class RpcProvider { //存储注册的服务列表
private static List<Object> serviceList; /**
* 发布rpc服务
* @param object
* @param port
* @throws Exception
*/
public static void export(int port,Object... services) throws Exception {
serviceList=Arrays.asList(services);
ServerSocket server = new ServerSocket(port);
Socket client = null;
while (true) {
//阻塞等待输入
client = server.accept();
//每一个请求,启动一个线程处理
new Thread(new ServerThread(client,serviceList)).start();
}
}
}

接下来ServerThread线程处理类的代码,ServerThread主要做以下几个步骤

  1. 读取客户端发送的服务名
  2. 判断服务是否发布
  3. 如果发布,则走反射逻辑,动态调用,返回结果
  4. 如果未发布,则返回提示通知
package com.rjzheng.rpc;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.List; public class ServerThread implements Runnable { private Socket client = null; private List<Object> serviceList = null; public ServerThread(Socket client, List<Object> service) {
this.client = client;
this.serviceList = service;
} @Override
public void run() {
ObjectInputStream input = null;
ObjectOutputStream output = null;
try {
input = new ObjectInputStream(client.getInputStream());
output = new ObjectOutputStream(client.getOutputStream());
// 读取客户端要访问那个service
Class serviceClass = (Class) input.readObject();
// 找到该服务类
Object obj = findService(serviceClass);
if (obj == null) {
output.writeObject(serviceClass.getName() + "服务未发现");
} else {
//利用反射调用该方法,返回结果
try {
String methodName = input.readUTF();
Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
Object[] arguments = (Object[]) input.readObject();
Method method = obj.getClass().getMethod(methodName, parameterTypes);
Object result = method.invoke(obj, arguments);
output.writeObject(result);
} catch (Throwable t) {
output.writeObject(t);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
client.close();
input.close();
output.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} } private Object findService(Class serviceClass) {
// TODO Auto-generated method stub
for (Object obj : serviceList) {
boolean isFather = serviceClass.isAssignableFrom(obj.getClass());
if (isFather) {
return obj;
}
}
return null;
} }

服务消费者

本部分的工程为battercake-consumer,项目结构图如下图所示

先上rpc框架调用部分的代码RpcConsumer,步骤分两步

  1. 封装一个代理类处理器
  2. 返回service的代理类对象
package com.rjzheng.rpc;

import java.lang.reflect.Proxy;

public class RpcConsumer {

    public static <T> T getService(Class<T> clazz,String ip,int port) {
ProxyHandler proxyHandler =new ProxyHandler(ip,port);
return (T)Proxy.newProxyInstance(RpcConsumer.class.getClassLoader(), new Class<?>[] {clazz}, proxyHandler);
}
}

接下来上代理类处理器的代码,代理类处理步骤分以下几步

  1. 建立socket连接
  2. 封装请求数据,发送给服务提供者
  3. 返回结果
package com.rjzheng.rpc;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.Socket; import com.rjzheng.service.BatterCakeService; public class ProxyHandler implements InvocationHandler { private String ip;
private int port; public ProxyHandler(String ip, int port) {
// TODO Auto-generated constructor stub
this.ip = ip;
this.port = port;
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
Socket socket = new Socket(this.ip, this.port);
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
try {
output.writeObject(proxy.getClass().getInterfaces()[0]);
output.writeUTF(method.getName());
output.writeObject(method.getParameterTypes());
output.writeObject(args);
output.flush();
Object result = input.readObject();
if(result instanceof Throwable) {
throw (Throwable) result;
}
return result;
} finally {
socket.shutdownOutput();
}
} }

接下来建立一个测试类RpcTest如下(跑该测试类前,记得运行在battercake-provider端的RpcBootstrap类发布BatterCakeService服务)

package com.rjzheng.start;

import com.rjzheng.rpc.RpcConsumer;
import com.rjzheng.service.BatterCakeService;
public class RpcTest { public static void main(String[] args) {
BatterCakeService batterCakeService=RpcConsumer.getService(BatterCakeService.class, "127.0.0.1", 20006);
String result=batterCakeService.sellBatterCake("双蛋");
System.out.println(result);
}
}

输出结果如下

双蛋煎饼,卖的特别好

至此,我们就实现了一个简易的rpc服务调用框架

自己动手实现RPC服务调用框架的更多相关文章

  1. 【原创】自己动手实现RPC服务调用框架

    自己动手实现rpc服务调用框架 本文利用java自带的socket编程实现了一个简单的rpc调用框架,由两个工程组成分别名为battercake-provider(服务提供者).battercake- ...

  2. java使用netty模拟实现一个类dubbo的分布式服务调用框架

    本文较长,如果想直接看代码可以查看项目源码地址: https://github.com/hetutu5238/rpc-demo.git 要想实现分布式服务调用框架,我们需要了解分布式服务一般需要的功能 ...

  3. NET Core微服务之路:自己动手实现Rpc服务框架,基于DotEasy.Rpc服务框架的介绍和集成

    本篇内容属于非实用性(拿来即用)介绍,如对框架设计没兴趣的朋友,请略过. 快一个月没有写博文了,最近忙着两件事;    一:阅读刘墉先生的<说话的魅力>,以一种微妙的,你我大家都会经常遇见 ...

  4. Spring Cloud微服务系列文,服务调用框架Feign

    之前博文的案例中,我们是通过RestTemplate来调用服务,而Feign框架则在此基础上做了一层封装,比如,可以通过注解等方式来绑定参数,或者以声明的方式来指定请求返回类型是JSON.    这种 ...

  5. RPC服务治理框架(一)RPC技术

    一.RPC是什么 remote procedure call:远程过程调用 过程就是程序,像调用本地方法一样调用远程的过程 RPC采用Client-Server结构,通过request-respons ...

  6. 服务调用框架DataStrom

    根据以前的命名服务,从新构建了下服务框架: 结构模式:c-center-s; 1.服务端: 服务端启动,讲自己的IP,端口注册到注册中心节点(master),然后注册自己的处理类(需要继承对应接口); ...

  7. 刚学会 C++ 的小白用这个开源框架,做个 RPC 服务要多久?

    本文适合有 C++ 基础的朋友 本文作者:HelloGitHub-Anthony HelloGitHub 推出的<讲解开源项目>系列,本期介绍基于 C++ 的 RPC 开源框架--rest ...

  8. Thrift 个人实战--Thrift RPC服务框架日志的优化

    前言: Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码. 不过Thrift的实现, 简单使用离实际生产环境还 ...

  9. 自研发RPC调用框架

    自主研发设计RPC远程调用框架,实现服务自动注册,服务发现,远程RPC调用,后续实现服务负载均衡 主要包括:客户端服务,服务端,服务发现,服务注册 github地址:https://github.co ...

随机推荐

  1. node的开发者环境设置丢失

    1.我看到的最简单的一种:evn 默认的值是  development var app = express(); if (app.get('env') === 'development') { req ...

  2. git 沙河游戏节点图, 自由沙盒模拟git, 各类交互git命令

    git学习练习总资源链接: https://try.github.io/ (练习已通,有document) 本沙盒游戏教学:https://learngitbranching.js.org/?demo ...

  3. 解决点击tomcat的startup.bat一闪而过的问题

    问题: 点击startup.bat一闪而过,经过网上查询,原来是没有配置JAVA_HOME,配置一下就可以了 原因: tomcat在启动时,会读取环境变量的信息,需要一个CATALINA_HOME 与 ...

  4. 在请求的参数中设置可选值列表为当前职责可访问的所有OU

    方法一: 实现此需求的前提之一是为该请求开启多业务实体访问,开启方法 系统管理员->系统管理->并发->程序,进入OAF页面,查询你的并发,然后点更新,选择请求,在业务实体模式下选择 ...

  5. node 使用范围 和 node的优势 (为什么 创业公司 选择的不是 java php ruby 等)

    链接 一些国外大公司 范围: 第一: 希望合并后台多个接口 成为一个接口,  或者频繁改动接口 相关, 比如数据 和数据格式之类,  后台难以配合, 这里可以使用node作为后台的应用层调用其他接口 ...

  6. myeclipse单步调试

    如何进行myclipse的单步调式与跟踪?希望大虾们详细点,多谢. 打断点,然后运行,进debug试图,按F6执行一行,按F5是钻进去执行 追问 朋友,能详细点吗? 本人是初学 回答 如图 如若成功请 ...

  7. 第24课 #pragma使用分析

    #pragma是C语言留给编译器厂商进行扩展用的. 这个关键字在不同的编译器之间也许是不能够移植的. #pragma简介 #pragma message #pragma message打印的消息并不代 ...

  8. mysql单列索引和联合索引的使用

    1,首先要确定优化的目标,在什么样的业务场景下,表的大小等等.如果表比较小的话,可能都不需要加索引. 2,哪些字段可以建索引,一般都where.order by 或者 group by 后面的字段. ...

  9. POJ2891 Strange Way to Express Integers

    题意 Language:Default Strange Way to Express Integers Time Limit: 1000MS Memory Limit: 131072K Total S ...

  10. BNF 和 ABNF 扩充巴科斯范式 了解

    BNF 巴科斯范式(BNF: Backus-Naur Form 的缩写)是由 John Backus 和 Peter Naur 首先引入的用来描述计算机语言语法的符号集.现在,几乎每一位新编程语言书籍 ...