项目3:从0开始的RPC框架(扩展版)

一. 全局配置加载

1. 需求分析

通常情况下,在RPC框架运行的会涉及到多种配置信息,比如注册中心的地址、序列化方式、网络服务端接口号等。

在简易版框架中,硬编码了这些配置,也就是都写死了,在真实的应用环境中是不利于维护和后期扩展的。同时RPC框架需要被其它项目引入,作为服务提供者和消费者沟通的桥梁,所以应当允许引入框架的项目通过编写配置文件来自定义配置。一般情况下,服务提供者与消费者需要编写相同的RPC配置。

综上,我们需要一套全局配置加载功能。能够让RPC框架轻松地从配置文件中读取配置,并且维护一个全局配置对象,便于框架快速获取到一致的配置。

2. 设计方案

(1)配置项

从最简单出发,先提供几个基础配置项:

  • name 名称
  • version 版本号
  • serverHost 服务器主机名
  • serverPort 服务器端口号

之后随着框架功能的扩展再不断增加新配置即可,比如注册中心地址、服务接口、序列化方式等。

可参考:Dubbo RPC框架的配置项。包括应用配置、协议配置、注册中心等。

(2)读取配置文件

配置文件的读取,使用Java的Properties类自行编写。通常情况下,读取的配置文件名称为application.properties,还可以通过指定文件名称后缀的方式来区分多环境,比如application-prod.properties表示生产环境,application-test.properties表示测试环境。

3. 具体实现

(1)项目初始化

创建khr-rpc-core模块,扩展版RPC项目都基于此模块进行。直接复制粘贴easy模块包并改名。

引入日志库(ch.qos.logback)和单元测试(junit)依赖,并将consumer和provider模块引入的RPC依赖都替换成khr-rpc-core。

(2)配置加载

创建配置类RpcConfig:

用于保存配置信息。

可以给属性指定一些默认值,

package com.khr.krpc.config;

import lombok.Data;

/**
 * RPC框架配置
 */
@Data
public class RpcConfig {

    /**
     * 名称
     */
    private String name = "k-rpc";

    /**
     * 版本号
     */
    private String version = "1.0";

    /**
     * 服务器主机名
     */
    private String serverHost = "localhost";

    /**
     * 服务器端口号
     */
    private String serverPort = "8080";
}

创建工具类ConfigUtils: 

用于读取配置文件并返回配置对象,可以简化调用。配置类应当尽量通用,不和业务强绑定。

之后调用ConfigUtils的静态方法loadConfig就能读取配置了。

package com.khr.krpc.utils;

import cn.hutool.core.util.StrUtil;
import cn.hutool.setting.dialect.Props;

/**
 * 配置工具类
 */
public class ConfigUtils {

    /**
     * 加载配置对象
     *
     * @param tClass
     * @param perfix
     * @param <T>
     * @return
     */
    public static <T> T loadConfig(Class<T> tClass,String perfix){
        return loadConfig(tClass,perfix,"");
    }

    /**
     * 加载配置对象,支持区分环境
     *
     * @param tClass
     * @param perfix
     * @param environment
     * @param <T>
     * @return
     */
    public static <T> T loadConfig(Class<T> tClass,String perfix,String environment){
        StringBuilder configFileBuilder = new StringBuilder("application");
        if (StrUtil.isNotBlank(environment)){
            configFileBuilder.append("-").append(environment);
        }
        configFileBuilder.append(".properties");
        Props props = new Props(configFileBuilder.toString());
        return props.toBean(tClass,perfix);
    }
}

创建RpcConstant接口:

用于存储RPC框架相关的常量。比如默认配置文件的加载前缀为rpc。

package com.khr.krpc.constant;

/**
 * RPC相关常量
 */
public interface RpcConstant {

    /**
     * 默认配置文件加载前缀
     */
    String DEFAULT_CONFIG_PREFIX = "rpc";
}

可以读取到类似下面的配置:

rpc.name=krpc
rpc.version=2.0
rpc.serverPort=8081

(3)维护全局配置对象

RPC框架中需要维护一个全局的配置对象。在引入RPC框架后并启动项目时,从配置文件中读取配置并创建对象实例,之后就可以集中地从这个对象中获取配置信息,而不需要每次加载配置时再重新读取并创建对象,减少了性能开销。

使用了设计模式中的单例模式。通常情况下会使用holder来维护全局配置对象实例,在本项目中使用RpcApplication类作为RPC项目的启动入口,并维护项目全局用到的变量。

package com.khr.krpc;

import com.khr.krpc.config.RpcConfig;
import com.khr.krpc.constant.RpcConstant;
import com.khr.krpc.utils.ConfigUtils;
import lombok.extern.slf4j.Slf4j;

/**
 * RPC框架应用
 * 相当于holder,存放了项目全局用到的变量。双检锁单例模式实现。
 */
@Slf4j
public class RpcApplication {

    private static volatile RpcConfig rpcConfig;

    /**
     * 框架初始化,支持传入自定义配置
     *
     * @param newRpcConfig
     */
    public static void init(RpcConfig newRpcConfig){
        rpcConfig = newRpcConfig;
        log.info("rpc init, config = {}",newRpcConfig.toString());
    }

    /**
     * 初始化
     */
    public static void init(){
        RpcConfig newRpcConfig;
        try {
            newRpcConfig = ConfigUtils.loadConfig(RpcConfig.class,RpcConstant.DEFAULT_CONFIG_PREFIX);
        }catch (Exception e){
            //配置加载失败,使用默认值
            newRpcConfig =new RpcConfig();
        }
        init(newRpcConfig);
    }

    /**
     * 获取配置
     *
     * @return
     */
    public static RpcConfig getRpcConfig(){
        if (rpcConfig == null){
            synchronized (RpcApplication.class){
                if (rpcConfig == null){
                    init();
                }
            }
        }
        return rpcConfig;
    }
}

 双检锁单例模式的经典实现,支持在获取配置时才调用init方法实现懒加载。

为了便于扩展,还支持自己传入配置对象,如果不传入的话,默认调用前面写好的ConfigUtils来加载配置。

之后一行代码即可正确加载配置:

RpcConfig rpc = RpcApplication.getRpcConfig();

4. 测试

(1)测试配置文件读取

在example-consumer模块的resources目录下编写配置文件application.properties,

rpc.name=krpc
rpc.version=2.0
rpc.serverPort=8081

创建ConsumerExample作为扩展版RPC项目的示例消费者类,测试配置文件读取,

package com.khr.example.consumer;

import com.khr.example.common.model.User;
import com.khr.example.common.service.UserService;
import com.khr.krpc.config.RpcConfig;
import com.khr.krpc.proxy.ServiceProxyFactory;
import com.khr.krpc.utils.ConfigUtils;

/**
 * 服务消费者示例
 */
public class ConsumerExample {

    public static void main(String[] args){

        RpcConfig rpc = ConfigUtils.loadConfig(RpcConfig.class,"rpc");
        System.out.println(rpc);
    }
}

读取结果为,

已成功读到配置文件中的内容。 

(2)测试全局配置对象加载

在example-provider模块中创建ProviderExample服务提供者示例类,能够根据配置动态地在不同端口启动Web服务

package com.khr.example.provider;

import com.khr.example.common.service.UserService;
import com.khr.krpc.RpcApplication;
import com.khr.krpc.registry.LocalRegistry;
import com.khr.krpc.server.HttpServer;
import com.khr.krpc.server.VertxHttpServer;

/**
 * 服务提供者示例
 */
public class ProviderExample {

    public static void main(String[] args){
        //RPC框架初始化
        RpcApplication.init();

        //注册服务
        LocalRegistry.registry(UserService.class.getName(),UserServiceImpl.class);

        //启动web服务
        HttpServer httpServer = new VertxHttpServer();
        httpServer.doStart(Integer.parseInt(RpcApplication.getRpcConfig().getServerPort()));
    }
}

启动结果为,

 

已成功在8080端口启动。 

至此,扩展功能,全局配置加载完成,后续可能会根据新增的功能逐步修改全局配置信息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/690394.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Halcon 双相机标定与拼图(二)

一、概述 这种标定有两种模式&#xff0c;有一个标定板和多个标定板两种 一个标定板 两个相机的重叠区域比较大&#xff0c;那么我们可以把标定板放到那个重叠区域来统一坐标系&#xff0c;如下 这种是只需要一个标定板&#xff0c;这种是推荐的方式 。这种是比较简单的&…

【JAVASE】日期与时间类(下)

三&#xff1a;LocalDateTime 相当于LocalDate类&#xff0c;在LocalDateTime类的对象中还可以封装时、分、秒和纳秒&#xff08;1纳秒是1秒的十亿分之一&#xff09;等时间类型。 例如&#xff0c;创建LocalDateTime对象 &#xff0c; LocalDateTime date LocalDateTi…

基本算法-枚举、模拟、递推(上)

目录 递归实现指数型枚举 题目描述 运行代码 代码思路 递归实现组合型枚举 题目描述 运行代码 代码思路 递归实现排列型枚举 题目描述 运行代码 代码思路 递归实现指数型枚举 题目描述 登录—专业IT笔试面试备考平台_牛客网 运行代码 #include<iostream> …

netty+springboot+vue聊天室(需要了解netty)

先看看这个使用websocket实现的聊天室&#xff0c;因为前端是使用websocket&#xff0c;和下面的demo的前端差不多就不解释实现原理&#xff0c;所以建议还是看看(要是会websocket的大佬请忽略) springbootwebsocketvue聊天室 目录 一、实现内容二、代码实现1.后端2.前端源码…

Java--命令行传参

1.有时你希望运行一个程序时再传递给它消息&#xff0c;这要靠传递命令行参数给main&#xff08;&#xff09;函数实现 2.选中文件右键找到如图选项并打开 3.在文件地址下输入cmd空格符号&#xff0c;再按回车调出命令窗口 4.如图一步步进行编译&#xff0c;在向其传入参数&…

什么情况下需要配戴助听器

以下几种情况需要考虑配戴助听器&#xff1a; 1、听力无波动3个月以上的感音神经性听力障碍。如:先天性听力障碍、老年性听力障碍、噪声性听力障碍、突聋的稳定期等&#xff0c;均可选配合适的助听器。 2、年龄方面。使用助听器没有严格的年龄限制&#xff0c;从出生数周的婴…

【Python】解决Python报错:ValueError: not enough values to unpack (expected 2, got 1)

​​​​ 文章目录 引言1. 错误详解2. 常见的出错场景2.1 函数返回值解包2.2 遍历含有不同长度元组的列表 3. 解决方案3.1 检查和调整返回值3.2 安全的解包操作 4. 预防措施4.1 使用异常处理4.2 单元测试 结语 引言 在Python编程中&#xff0c;ValueError 是一个常见的异常类…

win10重装系统?电脑系统重装一键清晰,干货分享!

在电脑的使用过程中&#xff0c;由于各种原因&#xff0c;我们可能会遇到系统崩溃、运行缓慢或者出现各种难以解决的问题。这时&#xff0c;重装系统往往是一个有效的解决方案。今天&#xff0c;我们就来详细介绍一下如何在Win10环境下进行系统的重装&#xff0c;帮助大家轻松解…

springboot+mqtt使用总结

1.软件的选型 1.1.使用免费版EMQX 1.1.1.下载 百度搜索的目前是会打开官网&#xff0c;这里提供下免费版的使用链接EMQX使用手册 文档很详细&#xff0c;这里不再记录了。 1.2.使用rabbitmq rabbitmq一般做消息队列用&#xff0c;作为mqtt用我没有找到详细资料&#xff0c…

[AIGC] SpringBoot的自动配置解析

下面是一篇关于SpringBoot自动配置的文章&#xff0c;里面包含了一个简单的示例来解释自动配置的原理。 SpringBoot的自动配置解析 Spring Boot是Spring的一个子项目&#xff0c;用于快速开发应用程序。它主要是简化新Spring应用的初始建立以及开发过程。其中&#xff0c;自动…

武汉理工大学 云计算与服务计算 期末复习

云计算与的定义 长定义是&#xff1a;“云计算是一种商业计算模型。它将计算任务分布在大量计算机构成的资源池上&#xff0c;使各种应用系统能够根据需要获取计算力、存储空间和信息服务。” 短定义是&#xff1a;“云计算是通过网络按需提供可动态伸缩的廉价计算服务。 云计…

批量转换更高效:一键修改TXT后缀名转DOCX,轻松实现文件高效管理!

在日常生活和工作中&#xff0c;我们经常需要处理大量的文件&#xff0c;而文件格式的转换和管理往往是其中一项繁琐的任务。特别是当需要将大量的TXT文件转换为DOCX格式时&#xff0c;传统的逐个手动操作不仅效率低下&#xff0c;还容易出错。然而&#xff0c;现在有了我们这款…

【ArcGIS微课1000例】0117:ArcGIS中如何将kml(kmz)文件转json(geojson)?

文章目录 一、kml获取方式二、kml转图层三、图层转json一、kml获取方式 kml文件是一种很常用的数据格式,可以从谷歌地球(googleearth)获取某一个地区的kml范围文件,如青海湖(做好的kml文件可以从配套实验数据包0117.rar中获取)。 二、kml转图层 打开【KML转图层】工具,…

在线渲染3d怎么用?3d快速渲染步骤设置

在线渲染3D模型是一种高效的技术&#xff0c;它允许艺术家和设计师通过互联网访问远程服务器的强大计算能力&#xff0c;从而加速渲染过程。无论是复杂的场景还是高质量的视觉效果&#xff0c;在线渲染服务都能帮助您节省宝贵的时间。 在线渲染3D一般选择的是&#xff1a;云渲染…

数据结构之初识泛型

目录&#xff1a; 一.什么是泛型 二.引出泛型 三.泛型语法及&#xff0c;泛型类的使用和裸类型(Raw Type) 的了解 . 四.泛型的编译&#xff1a; 五.泛型的上界 六.泛型方法 注意&#xff1a;在看泛型之前可以&#xff0c;回顾一下&#xff0c;包装类&#xff0c;包装类就是服务…

【Python预处理系列】深入理解过采样技术及其Python实现

目录 一、过采样简介 二、过采样的实现方法 三、过采样和欠采样是数据增强吗 四、Python实现SMOTE过采样 &#xff08;一) 生成不平衡数据集 &#xff08;二&#xff09; 将数据集转换为DataFrame&#xff0c;便于展示 &#xff08;三) 应用SMOTE算法进行过采样 &…

命令行打包最简单的android项目从零开始到最终apk文件

准备好需要的工具 AndroidDevTools - Android开发工具 Android SDK下载 Android Studio下载 Gradle下载 SDK Tools下载 jdk的链接我就不发出来,自己选择,我接下来用的是8版本的jdk和android10的sdk sdk的安装和环境变量的配置 sdk tool压缩包打开后是这样子,打开sdk mana…

3-1RT-Thread时钟管理

这里写自定义目录标题 时钟节拍是RT thread操作系统的最小时间单位。 第一个功能&#xff0c;rt tick值自动加1&#xff0c;在RT thread当中通过RT_USING_SMP定义了多核和单核的场景。第二个功能&#xff0c;检查当前线程的时间片&#xff0c;首先获取当前线程&#xff0c;将当…

[数据集][目标检测]室内积水检测数据集VOC+YOLO格式761张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;761 标注数量(xml文件个数)&#xff1a;761 标注数量(txt文件个数)&#xff1a;761 标注类别…

【Text2SQL 论文】PET-SQL:用 Cross-Consistency 的 prompt 增强的两阶段 Text2SQL 框架

论文&#xff1a;PET-SQL: A Prompt-enhanced Two-stage Text-to-SQL Framework with Cross-consistency ⭐⭐⭐ arXiv:2403.09732&#xff0c;商汤 & 北大 Code&#xff1a;GitHub 一、论文速读 论文一开始提出了以往 prompt-based 的 Text2SQL 方法的一些缺点&#xff1…