前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >手写RPC之基于TCP

手写RPC之基于TCP

原创
作者头像
用户10198968
发布于 2023-01-02 09:10:50
发布于 2023-01-02 09:10:50
4980
举报

手写RPC之基于TCP

0x01_解决服务在远端问题

image-20221227140546836
image-20221227140546836

代码结构:

代码语言:shell
AI代码解释
复制
│   │   ├── main
│   │   │   ├── java
│   │   │   │   └── com
│   │   │   │       └── bones
│   │   │   │           └── rpc
│   │   │   │               ├── IUserService.java
│   │   │   │               ├── User.java
│   │   │   │               ├── client
│   │   │   │               │   └── FgClient.java
│   │   │   │               └── server
│   │   │   │                   ├── FgServer.java
│   │   │   │                   └── UserServiceImpl.java

实体类User

代码语言:java
AI代码解释
复制
package com.bones.rpc;

import java.io.Serializable;

public class User implements Serializable {

    private static final long serialVersionUID = -1189354900598453538L;
    private Integer id;
    private String name;

    public User(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

接口IUserService

代码语言:java
AI代码解释
复制
package com.bones.rpc;

public interface IUserService {
    User findUserById(Integer id);
}

接口的实现server/UserServiceImpl

代码语言:java
AI代码解释
复制
package com.bones.rpc.server;

import com.bones.rpc.IUserService;
import com.bones.rpc.User;

public class UserServiceImpl implements IUserService {

    @Override
    public User findUserById(Integer id) {
        return new User(id,"Bones");
    }
}

服务端server/FgServer

代码语言:java
AI代码解释
复制
package com.bones.rpc.server;


import com.bones.rpc.IUserService;
import com.bones.rpc.User;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class FgServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket=new ServerSocket(9999);
        while (true){
            Socket socket=serverSocket.accept();
            process(socket);
            socket.close();
        }
    }
    private static void process(Socket socket) throws IOException {
        InputStream inputStream= socket.getInputStream();
        OutputStream outputStream=socket.getOutputStream();
        DataInputStream dataInputStream = new DataInputStream(inputStream);
        DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
        System.out.println("process");
        int id = dataInputStream.readInt();
        IUserService service=new UserServiceImpl();
        User user = service.findUserById(id);
        dataOutputStream.writeInt(user.getId());
        dataOutputStream.writeUTF(user.getName());
        dataOutputStream.flush();
    }
}

客户端client/FgClient

代码语言:java
AI代码解释
复制
package com.bones.rpc.client;

import com.bones.rpc.User;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public class FgClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 9999);
        // socket.getInputStream()

        ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
        DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
        dataOutputStream.writeInt(12);
        socket.getOutputStream().write(outputStream.toByteArray());
        socket.getOutputStream().flush();
        //dataOutputStream
        DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
        int id = dataInputStream.readInt();

        String name = dataInputStream.readUTF();
        User user=new User(id, name);
        System.out.println(user.getName());
        System.out.println(user.toString());
        outputStream.close();
        dataOutputStream.close();
        socket.close();
    }
}

0x02_解决客户端简单调用——引入存根stub

image-20221228145901902
image-20221228145901902

项目结构:

代码语言:shell
AI代码解释
复制
│   │   ├── main
│   │   │   ├── java
│   │   │   │   └── com
│   │   │   │       └── bones
│   │   │   │           └── rpc
│   │   │   │               ├── IUserService.java
│   │   │   │               ├── User.java
│   │   │   │               ├── client
│   │   │   │               │   ├── FgClient.java
│   │   │   │               │   └── Stub.java
│   │   │   │               └── server
│   │   │   │                   ├── FgServer.java
│   │   │   │                   └── UserServiceImpl.java

存根Stub

代码语言:java
AI代码解释
复制
package com.bones.rpc.client;

import com.bones.rpc.User;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;

public class Stub {
    public User findUserById(Integer id) throws Exception{
        Socket socket = new Socket("127.0.0.1", 9999);
        // socket.getInputStream()

        ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
        DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
        dataOutputStream.writeInt(id);
        socket.getOutputStream().write(outputStream.toByteArray());
        socket.getOutputStream().flush();
        //dataOutputStream.
        DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
        id = dataInputStream.readInt();

        String name = dataInputStream.readUTF();
        //User user=new User(id, name);
        outputStream.close();
        dataOutputStream.close();
        socket.close();

        return new User(id,name);
    }
}

客户端FgClient

代码语言:java
AI代码解释
复制
package com.bones.rpc.client;

import com.bones.rpc.User;
public class FgClient {
    public static void main(String[] args) throws Exception {
        Stub stub = new Stub();
        User user = stub.findUserById(124);
        System.out.println("user = " + user);
    }
}

客户端存根Stub:存放服务端的地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方。

0x03_客户端动态代理隐藏网络细节

image-20221228145827506
image-20221228145827506

Stub

代码语言:java
AI代码解释
复制
package com.bones.rpc.client;

import com.bones.rpc.IUserService;
import com.bones.rpc.User;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

public class Stub {

    static IUserService getStub(){
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = new Socket("127.0.0.1", 9999);
                // socket.getInputStream()

                ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
                DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
                dataOutputStream.writeInt(12);
                socket.getOutputStream().write(outputStream.toByteArray());
                socket.getOutputStream().flush();
                //dataOutputStream.
                DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
                int id = dataInputStream.readInt();

                String name = dataInputStream.readUTF();
                //User user=new User(id, name);
                outputStream.close();
                dataOutputStream.close();
                socket.close();

                return new User(id,name);
            }
        };

        //执行动态代理
        Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(), new Class[]{IUserService.class}, handler);

        return (IUserService) o;
    }

}

客户端调用

FgClient

代码语言:java
AI代码解释
复制
package com.bones.rpc.client;

import com.bones.rpc.IUserService;
import com.bones.rpc.User;


public class FgClient {
    public static void main(String[] args) throws Exception {
        IUserService service = Stub.getStub();
        User user = service.findUserById(13);
        System.out.println(user);
    }
}

<img src="https://wechat01.oss-cn-hangzhou.aliyuncs.com/img/image-20221228152552581.png" alt="image-20221228152552581" style="zoom:50%;" />

此时id是写死的,后面用反射就可以传入id了。

0x04_服务端反射提高灵活性

image-20221228152730360
image-20221228152730360

存根Stub

代码语言:java
AI代码解释
复制
package com.bones.rpc.client;

import com.bones.rpc.IUserService;
import com.bones.rpc.User;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

public class Stub {

    static IUserService getStub(){
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = new Socket("127.0.0.1", 9999);
                // socket.getInputStream()

                ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
                String methodName= method.getName();//方法
                Class[] parametersTypes = method.getParameterTypes();//参数类型
                //格式1:方法名、2、方法参数类型  3、参数值
                objectOutputStream.writeUTF(methodName);
                objectOutputStream.writeObject(parametersTypes);
                objectOutputStream.writeObject(args);
                objectOutputStream.flush();

                //dataOutputStream.
                DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
                int id = dataInputStream.readInt();

                String name = dataInputStream.readUTF();
                objectOutputStream.close();
                socket.close();

                return new User(id, name);
            }
        };

        //执行动态代理
        Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(), new Class[]{IUserService.class}, handler);

        return (IUserService) o;
    }
}

服务类FgServer

代码语言:java
AI代码解释
复制
package com.bones.rpc.server;


import com.bones.rpc.IUserService;
import com.bones.rpc.User;

import java.io.*;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;

public class FgServer {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket=new ServerSocket(9999);
        while (true){
            Socket socket=serverSocket.accept();
            process(socket);
            socket.close();
        }
    }
    private static void process(Socket socket) throws Exception {
        InputStream inputStream= socket.getInputStream();
        OutputStream outputStream=socket.getOutputStream();
        ObjectInputStream dataInputStream = new ObjectInputStream(inputStream);
        DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
        System.out.println("process");

        //格式1:方法名、2、方法参数类型  3、参数值
        String methodName = dataInputStream.readUTF();//方法名
        Class[] parametersTypes = (Class[]) dataInputStream.readObject();//方法参数类型
        Object[] args=(Object[]) dataInputStream.readObject();//参数值

        IUserService service=new UserServiceImpl();
        //反射
        Method method = service.getClass().getMethod(methodName, parametersTypes);
        User user = (User) method.invoke(service, args);
        dataOutputStream.writeInt(user.getId());
        dataOutputStream.writeUTF(user.getName());
        dataOutputStream.flush();
    }
}

测试,先运行服务端,再运行客户端:

代码语言:java
AI代码解释
复制
package com.bones.rpc.client;

import com.bones.rpc.IUserService;
import com.bones.rpc.User;


public class FgClient {
    public static void main(String[] args) throws Exception {
        IUserService service = Stub.getStub();
        User user = service.findUserById(137);
        System.out.println(user);
    }
}

<img src="https://wechat01.oss-cn-hangzhou.aliyuncs.com/img/image-20230101152550566.png" alt="image-20230101152550566" style="zoom:50%;" />

0x05_灵活的传递参数

image-20230101153852582
image-20230101153852582

存根Stub

代码语言:java
AI代码解释
复制
package com.bones.rpc.client;

import com.bones.rpc.IUserService;
import com.bones.rpc.User;

import java.io.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

public class Stub {

    static IUserService getStub(Class clazz){
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = new Socket("127.0.0.1", 9999);
                // socket.getInputStream()

                ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
                String methodName= method.getName();//方法
                Class[] parametersTypes = method.getParameterTypes();//参数类型
                //格式(0:类名,1:方法名、2、方法参数类型  3、参数值)
                objectOutputStream.writeUTF(clazz.getName());
                objectOutputStream.writeUTF(methodName);
                objectOutputStream.writeObject(parametersTypes);
                objectOutputStream.writeObject(args);
                objectOutputStream.flush();

                //dataOutputStream.
                ObjectInputStream dataInputStream = new ObjectInputStream(socket.getInputStream());

                Object o = dataInputStream.readObject();


                objectOutputStream.close();
                socket.close();

                return o;
            }
        };

        //执行动态代理
        Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(), new Class[]{IUserService.class}, handler);

        return (IUserService) o;
    }

}

FgServer

代码语言:java
AI代码解释
复制
package com.bones.rpc.server;


import com.bones.rpc.IUserService;
import com.bones.rpc.User;

import java.io.*;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;

public class FgServer {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket=new ServerSocket(9999);
        while (true){
            Socket socket=serverSocket.accept();
            process(socket);
            socket.close();
        }
    }
    private static void process(Socket socket) throws Exception {
        InputStream inputStream= socket.getInputStream();
        OutputStream outputStream=socket.getOutputStream();
        ObjectInputStream dataInputStream = new ObjectInputStream(inputStream);
        ObjectOutputStream dataOutputStream = new ObjectOutputStream(outputStream);
        System.out.println("process");

        //格式(0:类名,1:方法名、2、方法参数类型  3、参数值)
        String clazzName = dataInputStream.readUTF();
        String methodName = dataInputStream.readUTF();//方法名
        Class[] parametersTypes = (Class[]) dataInputStream.readObject();//方法参数类型
        Object[] args=(Object[]) dataInputStream.readObject();//参数值


        //反射
        //从服务的注册表中寻找(注册中心)
        Class clazz = UserServiceImpl.class;
        Method method = clazz.getMethod(methodName, parametersTypes);
        User user = (User) method.invoke(clazz.newInstance(), args);
        dataOutputStream.writeObject(user );
        dataOutputStream.flush();
    }
}

FgClient

代码语言:java
AI代码解释
复制
package com.bones.rpc.client;

import com.bones.rpc.IUserService;
import com.bones.rpc.User;


public class FgClient {
    public static void main(String[] args) throws Exception {
        IUserService service = Stub.getStub(IUserService.class);
        User user = service.findUserById(98);
        System.out.println(user);
    }
}

测试结果:

<img src="https://wechat01.oss-cn-hangzhou.aliyuncs.com/img/image-20230101154132739.png" alt="image-20230101154132739" style="zoom:50%;" />

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
springcloud集成elk
springcloud是一个应用很广的微服务全家桶,阿里开发了自己的springcloud alibaba分支,elk是一款不错的监控工具,尤其是日志分析,可以将springcloud微服务项目的日志通过logstash-logback-encoder传送到elk的logstash上,好了废话不多少了,直接进入环境搭建流程:
johnhuster的分享
2022/03/29
6730
springcloud集成elk
Springboot项目搭配ELK日志平台
上一篇讲过了elasticsearch和kibana的可视化组合查询,这一篇就来看看大名鼎鼎的ELK日志平台是如何搞定的。
天涯泪小武
2019/01/17
1.4K0
手把手教你搭建ELK
假如我订单服务总共部署在20台服务器上,如果我们直接登录服务器查询日志的话,那得先看一号机器,没有再看二号机器……并且日志量较多的话,可能一个时间段内日志就被切分成好几个文件,这样查询实在是太麻烦了,ELK就是解决这个问题的。
贪挽懒月
2022/12/02
1.3K0
Spring boot with ELK(Elasticsearch + Logstash + Kibana)
本文节选自《Netkiller Java 手札》 5.31. Spring boot with ELK(Elasticsearch + Logstash + Kibana) 将 Spring boot 日志写入 ELK 有多种实现方式,这里仅提供三种方案: Spring boot -> logback -> Tcp/IP -> logstash -> elasticsearch 这种方式实现非常方便不需要而外包或者软件 Spring boot -> logback -> Redis -> logstash
netkiller old
2018/03/06
2.1K0
ELK实战(Springboot日志输出查找)
需求 把分布式系统,集群日志集中处理快速查询 搭建ELK并与springboot日志输出结合 搭建ELK 基于我前面的elasticsearch搭建博客文档docker-compose.yml基础上进行添加修改 新建docker-compose.yml文件,内容如下 version: '2' services: elasticsearch-central: image: elasticsearch:5.6.4 container_name: es1 volumes:
老梁
2019/09/10
1.8K0
ELK实战(Springboot日志输出查找)
Spring Cloud构建微服务架构:分布式服务跟踪(整合logstash)【Dalston版】
通过之前的《入门示例》,我们已经为两个由SpringCloud构建的微服务项目 trace-1和 trace-2引入了Spring Cloud Sleuth的基础模块 spring-cloud-starter-sleuth,实现了为各微服务的日志信息中添加跟踪信息的功能。但是,由于日志文件都离散的存储在各个服务实例的文件系统之上,仅仅通过查看日志文件来分析我们的请求链路依然是一件相当麻烦的差事,所以我们还需要一些工具来帮助我们集中的收集、存储和搜索这些跟踪信息。引入基于日志的分析系统是一个不错的选择,比如:
程序猿DD
2018/03/20
1.4K0
Spring Cloud构建微服务架构:分布式服务跟踪(整合logstash)【Dalston版】
Spring Cloud 微服务(七)- 上:日志收集集成
每次看日志信息都需要登陆到远程服务器,会很麻烦,而且不同应用的日志需要切换到不同的日志文件,有时候还要联合多个日志文件查看请求涉及的所有信息。总结下来主要有 3 点问题:
安宁
2020/07/24
2.7K0
基于ELK收集业务日志的初步体验
elasticsearch - 7.5.1 logstash - 7.5.1 kibana - 7.5.1 logback.properties # 应用名称 appName=data-center
用户1215919
2021/12/28
3320
基于ELK收集业务日志的初步体验
SpringCloud——Sleuth、ELK、Zipkin
【解释】INFO [simple-demo-2,ddfe378c0a8ec7cc,d4f2e63ad9bc890b,true]
爪哇缪斯
2023/05/10
8650
SpringCloud——Sleuth、ELK、Zipkin
Java 项目日志:从Logback到SLF4J,再到链路跟踪配置详解
Java 应用开发运维中,日志记录重要。本文探讨 Logback 与 SLF4J 使用方式,介绍如何实现链路跟踪功能,提升系统监控和问题排查能力。
Yeats_Liao
2025/01/07
3880
Java 项目日志:从Logback到SLF4J,再到链路跟踪配置详解
项目实战|史上最简单的springboot 整合elk教程,实现日志收集
总有测试小姐姐教你紧急刹车,回头做(改)人(bug):AI大师,你这不行啊!(吃瓜群众排排坐,笑歪了嘴)
AI码师
2021/01/27
3.7K0
项目实战|史上最简单的springboot 整合elk教程,实现日志收集
Spring Cloud Sleuth使用ELK收集&分析日志
注意, logstash-logback-encoder 的版本务必和Logback兼容,否则会导致应用启动不起来,而且不会打印任何日志!可前往 https://github.com/logstash/logstash-logback-encoder 查看和Logback的兼容性。
用户1516716
2019/09/02
1.8K0
Spring Cloud Sleuth使用ELK收集&分析日志
【Spring Boot】深入解密Spring Boot日志:最佳实践与策略解析
Spring Boot默认使用SLF4J作为日志门面,并集成了Logback作为日志实现。SLF4J(Simple Logging Facade for Java)是一个通用的日志抽象层,可以与多种日志框架结合使用,如Logback、Log4j、Java Util Logging(JUL)等。Logback是一个快速、灵活且功能强大的日志框架,是Log4j的继任者。
IT_陈寒
2024/05/24
8190
【Spring Boot】深入解密Spring Boot日志:最佳实践与策略解析
SpringCloud学习5-如何创建一个服务提供者provider
前几篇主要集中在注册中心eureka的使用上,接下来可以创建服务提供者provider来注册到eureka。 demo源码见: https://github.com/Ryan-Miao/spring-cloud-Edgware-demo/tree/master/provider-demo 为了方便版本控制,接下来的项目都是基于https://github.com/Ryan-Miao/spring-cloud-Edgware-demo 这个parent配置的。 创建子moudle provider-demo
Ryan-Miao
2018/07/04
5120
如何监视所有 Spring Boot 微服务?
Spring Boot Actuator 是一个非常强大的工具,可以提供生产就绪的特性,如健康检查、度量指标、审计等。
代码小李
2025/02/02
610
分布式日志系统:Plumelog部署及系统接入
第一步:安装 redis 或者 kafka(一般公司redis足够) redis 官网:https://redis.io kafka:http://kafka.apache.org
Freedom123
2024/03/29
1.3K0
分布式日志系统:Plumelog部署及系统接入
SpringBoot应用整合ELK实现日志收集
搭建了ELK日志收集系统之后,我们如果要查看SpringBoot应用的日志信息,就不需要查看日志文件了,直接在Kibana中查看即可。
macrozheng
2019/07/22
2.3K0
SpringBoot应用整合ELK实现日志收集
SpringBoot集成ELK实现日志收集实践
Elasticsearch默认使用mmapfs目录来存储索引。操作系统默认的mmap计数太低可能导致内存不足,我们可以使用下面这条命令来增加内存:
gang_luo
2020/08/17
1.3K0
Spring Boot 整合 elk
进入容器后,修改 /etc/logstash/conf.d/02-beats-input.conf
程序员果果
2019/05/16
8640
Spring Boot ELK 整体介绍 及使用
1. ELK应用场景 在复杂的企业应用服务群中,记录日志方式多种多样,并且不易归档以及提供日志监控的机制。无论是开发人员还是运维人员都无法准确的定位服务、服务器上面出现的种种问题,也没有高效搜索日志内容从而快速定位问题的方式。因此需要一个集中式、独立的、搜集管理各个服务和服务器上的日志信息,集中管理,并提供良好的UI界面进行数据展示,处理分析。 因此:ELK提供一套开源的解决方案,能高效、简便的满足以上场景。
Freedom123
2024/03/29
1480
Spring Boot ELK 整体介绍 及使用
推荐阅读
相关推荐
springcloud集成elk
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文