项目笔记——简易RPC框架(待升级)

后续会迭代

模块目录

在这里插入图片描述

协议模块proto:定义了其他模块需要的bean,如ip端口,请求信息,响应信息,服务信息(注册的key)

序列化模块codec:定义了序列化和反序列化方法

网络模块transport:实现底层的网络通信,使用了jetty

server模块:rpc server端,接收请求报文并返回响应报文

client模块:rpc client端,发送请求报文收到响应报文

工具模块common: 反射工具类

流程

测试实例

服务端

public static void main(String[] args) throws Exception {
        RpcServer server = new RpcServer();
        server.register(CalcService.class,new CalcServiceImpl());
        server.start();
    }

客户端

public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        RpcClient client = new RpcClient();   
        CalcService  service = client.getProxy(CalcService.class);      
        int r1 = service.add(1,2);
        int r2 = service.minus(10,8);
        System.out.println(r1);
        System.out.println(r2);
    }

调用类

public class CalcServiceImpl implements CalcService{
    @Override
    public int add(int a, int b) {
        return a+b;
    }

    @Override
    public int minus(int a, int b) {
        return a-b;
    }
}
服务端初始化

package: rpc-server class:RpcServer

public RpcServer() throws InstantiationException, IllegalAccessException {
        this.config=new RpcServerConfig(); //1
        this.net=ReflectionUtils.newInstance(config.getTransportClass());
        this.net.init(config.getPort(),this.handler);//2
        this.ecoder=ReflectionUtils.newInstance(config.getEcoderClass());//3
        this.dcoder=ReflectionUtils.newInstance(config.getDcoderClass());
        this.serviceInvoker=new ServiceInvoker();
        this.serviceManager=new ServiceManager();
    }
1.配置端口
this.config=new RpcServerConfig();

package: rpc-server class:RpcServerConfig

定义服务端port

public class RpcServerConfig {
    private Class<? extends TransportServer> transportClass=HttpTransportServer.class;
    private Class<? extends Ecoder>ecoderClass =JsonEcoder.class;
    private Class<? extends Dcoder>dcoderClass= JsonDcoder.class;
    private int port=3001;

}
2.网络处理模块初始化
this.net=ReflectionUtils.newInstance(config.getTransportClass());
this.net.init(config.getPort(),this.handler);

package:rpc-transport class:HttpTransportServer

init函数

使用jetty(服务端的web容器类似tomcat,接收http)

@Override
public void init(int port, RequestHandler requestHandler) {

    this.requestHandler=requestHandler;
    this.server=new Server(port);
    //接收请求并处理

    ServletContextHandler servletContextHandler=new ServletContextHandler();
    server.setHandler(servletContextHandler);
    ServletHolder servletHolder=new ServletHolder(new RequestServlet() );
    servletContextHandler.addServlet(servletHolder,"/*");
}

package:rpc-transport class:HttpTransportServer

init()创建的servlet doPost函数用于接收处理网络请求

class RequestServlet extends HttpServlet
    {
        @SneakyThrows
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
            log.info("client log");
            InputStream inputStream=req.getInputStream();
            OutputStream outputStream=resp.getOutputStream();
            if(requestHandler!=null)
            {
                requestHandler.onRequest(inputStream,outputStream);
            }
            outputStream.flush();
        }

    }
3.实例化后续用到的类
注册服务

package:rpc-server class:ServiceManager

public ServiceManager(){
        this.services=new ConcurrentHashMap<>();
    }

    public <T> void register(Class<T> interfaceClass,T bean)
    {
        Method[] methods=ReflectionUtils.getPublicMethod(interfaceClass);
        for(Method method:methods)
        {
            ServiceInstance serviceInstance=new ServiceInstance(bean,method); //1
            ServiceDescriptor serviceDescriptor=ServiceDescriptor.from(interfaceClass,method);//2
            services.put(serviceDescriptor,serviceInstance);
            System.out.println(serviceDescriptor.getClazz());
            log.info("register————————{} {}",serviceDescriptor.getClazz(),serviceDescriptor.getMethod());
        }
    }
1.服务信息作为map的key
ServiceDescriptor serviceDescriptor=ServiceDescriptor.from(interfaceClass,method);//2

package:rpc-proto class:ServiceDescriptor

服务的类名,方法名,返回类型,参数

 private String clazz;
 private String method;
 private String returnType;
 private String[] parameterType;
2.具体服务实例为map的value
erviceInstance serviceInstance=new ServiceInstance(bean,method); //1

package:rpc-server class: ServiceInstance

bean实例和方法实例作为后续反射调用服务(方法)的基础

@Data
@AllArgsConstructor
public class ServiceInstance {
    private Object target;
    private Method method;
}
客户端初始化
public RpcClient(RpcClientConfig rpcClientConfig) throws InstantiationException, IllegalAccessException {
        this.config=rpcClientConfig; //1
        this.ecoder=ReflectionUtils.newInstance(this.config.getEcoderClass());
        this.dcoder=ReflectionUtils.newInstance(this.config.getDcoderClass());
        this.selector=ReflectionUtils.newInstance(this.config.getSelectorClass());

        this.selector.init(
                this.config.getServers(),
                this.config.getConnectCount(),
                this.config.getTransportClass()
        );
    }
1.创建多个和服务端连接
 this.config=rpcClientConfig;

package:rpc-client class:RpcClientConfig

 private List<Peer> servers = Arrays.asList(new Peer("127.0.0.1",3001));
2.连接选择类的初始化
this.selector=ReflectionUtils.newInstance(this.config.getSelectorClass());

        this.selector.init(
                this.config.getServers(),
                this.config.getConnectCount(),
                this.config.getTransportClass()
        );
    }

package:rpc-client class:RandomTransportSelector

后续连接时会在连接池中随机取出一个作为通信连接

private List<TransportClient> clients;

public RandomTransportSelector()
{
     clients=new ArrayList<>();
}

@Override
    public synchronized void init(List<Peer> peers, int count, Class<? extends TransportClient> clazz) throws InstantiationException, IllegalAccessException {
        count=Math.max(count,1);
        for(Peer peer:peers) {
            for (int i = 0; i < count; i++) {
                TransportClient client=ReflectionUtils.newInstance(clazz);
                client.connect(peer);
                clients.add(client);
            }
            log.info("connect server:{}",peer);
        }
    }
方法调用之动态代理
客户端获取代理实例

package:rpc-client class:RpcClient

public <T> T getProxy(Class<T> clazz){
    System.out.println("enter getProxy");
    return (T)Proxy.newProxyInstance(
            getClass().getClassLoader(),
            new Class[]{clazz},
            new RemoteInvoker(clazz,ecoder,dcoder,selector)

    );
}
客户端远程调用

package:rpc-client class:RemoteInvoker

动态代理的核心函数

将请求服务信息(方法所在类,方法名,参数等)封装在request对象中

request通过invokeRemote发送给服务端执行,服务端执行后的结果返回给客户端

从而完成了客户端远程方法调用(传输过程见后续)

 @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException {
       
        Request request=new Request();
        request.setServiceDescriptor(ServiceDescriptor.from(clazz,method));
        request.setParameter(args);
        Response response=invokeRemote(request);
        if(response==null||response.getCode()!=0)
        {
            System.out.println("response is null");
            throw new IllegalAccessException("fail to remoteInvoke");
        }
        return response.getData();

    }
服务端执行服务

package:rpc-server class:RpcServer

通过lookup函数(注册服务map的get的方法)通过request提取的serviceDescriptor(map的key)找到服务实例

通过反射执行serviceInstance,方法执行得到的结果封装为response发给客户端

(传输过程见后续)

private RequestHandler handler = new RequestHandler() {

        @Override
        public void onRequest(InputStream inputStream, OutputStream outputStream) throws IOException {
            Response response=new Response();
            try {
                byte[] bytes = IOUtils.readFully(inputStream, inputStream.available());
                Request request = dcoder.decode(bytes, Request.class);
                log.info("get request", request);
                ServiceInstance serviceInstance = serviceManager.lookup(request);
                Object ret = serviceInvoker.serviceInvoke(serviceInstance, request);
                response.setData(ret);
            }
            catch(Exception e)
            {

                response.setCode(1);
                response.setMessage("RpcSever error "+e.getClass().getName());
            }
            finally {
                byte outBytes[]=ecoder.ecode(response);
                outputStream.write(outBytes);
            }
        }
    };

package:rpc-server class:ServiceManage

public ServiceInstance lookup(Request request)
{
    ServiceDescriptor serviceDescriptor=request.getServiceDescriptor();
    return services.get(serviceDescriptor);
}
方法调用之网络传输过程及序列化

package:rpc-client class:remoteInvoke 函数:invokeRemote(Request request)——前面客户端远程调用中使用

byte[] outBytes=ecoder.ecode(request);
InputStream responseStream=transportClient.write(new ByteArrayInputStream(outBytes));
byte[] inBytes= IOUtils.readFully(responseStream,responseStream.available());
response=dcoder.decode(inBytes,Response.class);
序列化
byte[] outBytes=ecoder.ecode(request);

package:rpc-codec class:JsonEcoder

使用Json序列化,将request序列化成二进制数组

 private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    public byte[] ecode(Object obj) {
        try {
            return objectMapper.writeValueAsBytes(obj);
        } catch (JsonProcessingException e) {
            log.error("序列化时有错误发生: {}", e.getMessage());
            e.printStackTrace();
            return null;
        }
    }
IO传输

客户端:

InputStream responseStream=transportClient.write(new ByteArrayInputStream(outBytes));

建立连接,客户端通过IO输出流将二进制数组输出,并获得服务端传来的输入流

package:rpc-transport class:HttpTransportClient

public InputStream write(InputStream data) throws IOException {
        try {
            HttpURLConnection httpURLConnection = (HttpURLConnection) (new URL(url).openConnection());
            httpURLConnection.setDoInput(true); //设置是否向HttpURLConnection输入
            httpURLConnection.setDoOutput(true); //设置是否向HttpURLConnection输出
            httpURLConnection.setUseCaches(false); //设置是否使用缓存
            httpURLConnection.setRequestMethod("POST");
            httpURLConnection.connect();//建立连接
            IOUtils.copy(data, httpURLConnection.getOutputStream());//将data作为请求发送出去
            int resultCode = httpURLConnection.getResponseCode();//收到响应信息获取状态码
            System.out.println("resultCode "+resultCode);
            //System.out.println("HTTP");
            //System.out.println("Input "+httpURLConnection.getInputStream());
            if (resultCode == HttpURLConnection.HTTP_OK) {
                System.out.println("HTTPOK");
                return httpURLConnection.getInputStream();
            } else
                return httpURLConnection.getInputStream();
        }catch (IOException e)
        {
            throw new IllegalStateException();
        }
    }
public void connect(Peer peer) {
        this.url="http://"+peer.getHost()+":"+peer.getPort();

    }

服务端:

package:rpc-transport class:HttpTransportServer

requestHandler.onRequest(inputStream,outputStream);

package:rpc-transport class:HttpTransportServer

先将客户端发来input输入流的二进制数据读取出来,然后反序列化成Request类

进行中间处理(前文已说明)

然后将结果序列化为二进制数组,以输出流返回客户端

 public void onRequest(InputStream inputStream, OutputStream outputStream) throws IOException {
            Response response=new Response();
            try {
                byte[] bytes = IOUtils.readFully(inputStream, inputStream.available());
                Request request = dcoder.decode(bytes, Request.class);
                log.info("get request", request);
                ServiceInstance serviceInstance = serviceManager.lookup(request);
                Object ret = serviceInvoker.serviceInvoke(serviceInstance, request);
                response.setData(ret);
            }
            catch(Exception e)
            {

                response.setCode(1);
                response.setMessage("RpcSever error "+e.getClass().getName());
            }
            finally {
                byte outBytes[]=ecoder.ecode(response);
                outputStream.write(outBytes);
            }
        }
反序列化
response=dcoder.decode(inBytes,Response.class);

package:rpc-codec class:JsonDcoder

public <T> T decode(byte[] bytes, Class<T> clazz) {
        try {
            Object obj = objectMapper.readValue(bytes, clazz);

            return (T)obj;
        } catch (IOException e) {
            log.error("反序列化时有错误发生: {}", e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

知识扩展

Jetty
项目中的使用
this.server=new Server(port);
        //接收请求并处理
        ServletContextHandler servletContextHandler=new ServletContextHandler();
        server.setHandler(servletContextHandler);
        ServletHolder servletHolder=new ServletHolder(new RequestServlet() );
        servletContextHandler.addServlet(servletHolder,"/*");
什么是Jetty?

​ 简单来讲Jetty就是一个开源的HTTP服务器和Servlet引擎,它可以为JSP和Servlet提供运行时环境,比如Java Web应用最常用的Servlet容器Tomcat,由于其轻量、灵活的特性,Jetty也被应用于一些知名产品中,例如ActiveMQ、Maven、Spark、GoogleAppEngine、Eclipse、Hadoop等。

为什么使用Jetty?

​ ①异步的 Servlet,支持更高的并发量

​ ②模块化的设计,更灵活,更容易定制,也意味着更高的资源利用率

​ ③在面对大量长连接的业务场景下,Jetty 默认采用的 NIO 模型是更好的选择

​ ④将jetty嵌入到应用中,使一个普通应用可以快速支持 http 服务

架构

jetty 的架构比较简单,核心组件主要是由 Server 和 Handler 组成。其中 Server 的 Handler 是其比较重要的一个数据模型,Jetty 中所有的组件都是基于 Handler 来实现的。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Jetty 欢迎访问Jetty文档 Wiki. Jetty是一个开源项目,提供了http服务器、http客户端和java servlet容器。 这个wiki提供jetty的入门教程、基础配置、功能特性、优化、安全、JavaEE、监控、常见问题、故障排除帮助等等。它包含教程、使用手册、视频、特征描述、参考资料以及常见问题。 Jetty文档 ---------------- 入门: 下载Download, 安装, 配置, 运行 Jetty入门(视频) 下载和安装Jetty 如何安装一个Jetty包 如何配置Jetty – 主要文档 如何运行Jetty 用JConsole监控Jetty 如何使用Jetty开发 Jetty HelloWorld教程 Jetty和Maven HelloWorld教程 Jetty(6)入门 (www.itjungle.com) Jetty Start.jar 配置Jetty 如何设置上下文(Context Path) 如何知道使用了那些jar包 如何配置SSL 如何使用非root用户监听80端口 如何配置连接器(Connectors) 如何配置虚拟主机(Virtual Hosts) 如何配置会话ID(Session IDs) 如何序列化会话(Session) 如何重定向或移动应用(Context) 如何让一个应用响应一个特定端口 使用JNDI 使用JNDI 在JNDI中配置数据源(DataSource) 内嵌Jetty服务器 内嵌Jetty教程 内嵌Jetty的HelloWorld教程 内嵌Jetty视频 优化Jetty 如何配置垃圾收集 如何配置以支持高负载 在Jetty中部署应用 部署管理器 部署绑定 热部署 Context提供者 如何部署web应用 webApp提供者 如何部署第三方产品 部署展开形式的web应用 使用Jetty进行开发 如何使用Jetty进行开发 如何编写Jetty中的Handlers 使用构建工具 如何在Maven中使用Jetty 如何在Ant中使用Jetty Maven和Ant的更多支持 Jetty Maven插件(Plugin) Jetty Jspc Maven插件(Plugin) Maven web应用工程原型 Ant Jetty插件(Plugin) 使用集成开发环境(IDEs) 在Eclipse中使用Jetty 在IntelliJ中使用Jetty 在Eclipse中工作 在Eclipse中开发Jetty Jetty WTP插件(Plugin) JettyOSGi SDK for Eclipse-PDE EclipseRT Jetty StarterKit SDK OSGi Jetty on OSGi, RFC66 基于Jetty OSGi的产品 OSGi贴士 Equinox中使用Jetty实现HTTP Service Felix中使用Jetty实现HTTP Service PAX中使用Jetty实现HTTP Srevice ProSyst mBedded Server Equinox Edition Spring Dynamic Modules里的Jetty JOnAS5里的Jetty 配置Ajax、Comet和异步Servlets 持续和异步Servlets 100 Continue和102 Processing WebSocket Servlet 异步的REST Stress Testing CometD 使用Servlets和Filters Jetty中绑定的Servlets Quality of Service Filter Cross Origin Filter 配置安全策略(Security Policies) 安全领域(Security Realms) 安全域配置教程 Java Authentication and Authorization Service (JAAS) JAAS配置教程 JASPI 安全模型(Secure Mode) 存储在文件中的安全密码以及编程教程 如何开启或禁止Jetty中的SSL功能 如何在Jetty中安全存储密码 如何安全终止Jetty 如何配置Spnego Application Server Integrations(集成) Apache Geronimo JEE 配置Apache httpd和Jetty教程 配置Apache mod_proxy和Jetty 配置Jetty中的AJP13 在JBoss中配置Jetty Remote Glassfish EJBs from Jetty Jetty and Spring EJB3 (Pitchfork) JBoss EJB3 ObjectWeb EasyBeans

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值