手写RPC(十) 优化

经过了前面的九节(千难万阻)我们终于拿到了想要的结果,但是这就结束了吗?作为一个合格的程序员,一定要学会对需求说不!,呃,这也没喝酒呀,怎么就吐真言了呢?作为一个合格的程序员,一定要学会对不好的代码说不!

在这里插入图片描述

比如我们服务端收到请求以后,反射调用方法时生成对象的时候需要依赖相关的配置(META-INF/services),每次调用都会生成一个新的对象(clz.newINSTANCE()) ,作为一个有追求的程序员。这怎么能忍!
spring的作用之一不就是管理bean的生命周期么?说干就干!
pom.xml声明 spring 的依赖

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.5.5</version>
</dependency>

rpc-portocol模块引入声明的 spring 依赖

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
</dependency>

spring的环境有了,需要我们把需要托管给spring的bean交给spring,这里我们给Dog添加对应的注解@Service

package com.info.provider.service.impl;

import com.info.api.Animal;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class Dog implements Animal {
    
    

    @Override
    public String run() {
    
    
        String msg = "小狗跑起来一蹦一跳...";
        log.info(msg);
        return msg;
    }

    @Override
    public String speak() {
    
    
        String msg = "小狗叫起来汪汪汪...";
        log.info(msg);
        return msg;
    }
}

然后提供一个 bean的管理类SpringBeanManager.java

package com.info.protocol;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringBeanManager implements ApplicationContextAware {
    
    

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
    
        SpringBeanManager.applicationContext = applicationContext;
    }

    public static <T> T getBean(Class<T> clazz) {
    
    
        return applicationContext.getBean(clazz);
    }
}

这样我们的bean就托管给spring了,我们需要bean的时候也可以找spring去获取。

修改服务端反射调用的逻辑CustomServerHandler.javainvoke 方法

// 调用目标方法
    private Object invoke(Request request) {
    
    
        final String clzName = request.getClassName();
        try {
    
    
            final Class<?> clz = Class.forName(clzName);
            final Object instance = SpringBeanManager.getBean(clz);
            final Method method = clz.getDeclaredMethod(request.getMethodName(), request.getParameterTypes());
            return method.invoke(instance, request.getParams());
        } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
    
    
            e.printStackTrace();
        }
        return null;
    }

这时候provider的启动类代码可以调整为springboot的启动方式并指定扫描的包路径ProviderApplication.java

package com.info.provider;

import com.info.protocol.netty.server.NettyServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackages = "com.info")
@SpringBootApplication
public class ProviderApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(ProviderApplication.class, args);
        new NettyServer("localhost", 9000).startServer();
    }
}

变异 --> 打包 --> 启动项目发现会有日志冲突的问题,因为spring-boot-starter中已经有了日志的依赖,因此可以去除我们之前的日志相关依赖。去除相关pom中以下依赖

<!--    日志实现    -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
        </dependency>

去除后再次运行项目,一切OK!搞定!

扫描二维码关注公众号,回复: 13576952 查看本文章

至此,我们一个简单的rpc框架的雏形已经完成了,完成一个框架的从头到尾的开发,我相信你可以收货不少东西,但是你以为这就结束了?
在这里插入图片描述

  • 作为一款优秀的rpc框架,只能单机环境玩,这怎么能忍!
  • 既然要效率,传输数据量大的时候真的不考虑压缩嘛!
  • 突然发生网络抖动,要不要重试!
  • 服务提供者挂了,请求还发过来怎么办!
  • ......

正餐才刚刚开始!

PS:简单版 rpc 框架所有代码已经上传至gitee (click me!) (简单版 rpc 实现 tag v1.o),需要的自取。
在这里插入图片描述

系列文章传送门如下:
手写RPC(一) 絮絮叨叨
手写RPC(二) 碎碎念
手写RPC(三) 基础结构搭建
手写RPC(四) 核心模块网络协议模块编写 ---- netty服务端
手写RPC(五) 核心模块网络协议模块编写 ---- 自定义协议
手写RPC(六) 核心模块网络协议模块编写 ---- 实现编解码器
手写RPC(七) 核心模块网络协议模块编写 ---- 实现客户端
手写RPC(八) provider、consumer 实现
手写RPC(九) 测试
关于 LengthFieldBasedFrameDecoder 不得不说的事

猜你喜欢

转载自blog.csdn.net/hxj413977035/article/details/121630781