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

news2024/11/29 8:51:54

一. 全局配置加载

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端口启动。 

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java——数组排序

一、排序介绍 1、排序的概念 排序是将多个数据按照指定的顺序进行排列的过程。 2、排序的种类 排序可以分为两大类&#xff1a;内部排序和外部排序。 3、内部排序和外部排序 1&#xff09;内部排序 内部排序是指数据在内存中进行排序&#xff0c;适用于数据量较小的情况…

开源VS闭源:大模型发展路径之争,你站哪一派?

文章目录 引言一、数据隐私1.1开源大模型的数据隐私1.2 闭源大模型的数据隐私1.3 综合考量 二、商业应用2.1 开源大模型的商业应用2.2 闭源大模型的商业应用2.3 商业应用的综合考量 三、社区参与3.1 开源大模型的社区参与3.2 闭源大模型的社区参与3.3 综合考量 结论 引言 在人…

问题:下列可以作为机组投运凝结水精处理系统的指标为()。 #学习方法#经验分享#微信

问题&#xff1a;下列可以作为机组投运凝结水精处理系统的指标为&#xff08;&#xff09;。 A.启动分离器出水含铁量小于1000ug/L B.启动分离器出水含铁量大于1000ug/L C.启动分离器出水含铁量等于1000ug/L D.以上都不是 参考答案如图所示

最佳实践的实践 - API 不应将 HTTP 重定向到 HTTPS

原文&#xff1a;jviide - 2024.05.23 TL;DR: 与其将 API 调用从 HTTP 重定向到 HTTPS&#xff0c;不如让失败显而易见。要么完全禁用 HTTP 接口&#xff0c;要么返回明确的 HTTP 错误响应&#xff0c;并撤销通过未加密连接发送的 API 密钥。遗憾的是&#xff0c;许多知名的 A…

小白的学习资料:Spark MLlib 机器学习详细教程

Spark MLlib 机器学习详细教程 Apache Spark 是一个强大的开源分布式计算框架&#xff0c;广泛用于大数据处理和分析。Spark 提供了丰富的库&#xff0c;其中 MLlib 是其机器学习库&#xff0c;专为大规模数据处理设计。本教程将详细介绍 Spark MLlib&#xff0c;包括其主要功…

公司面试题总结(二)

7. 说说 JavaScript 中的数据类型&#xff1f;存储上的差别&#xff1f; • 基本类型&#xff1a; o Number o String o Boolean o Undefined o null o symbol • 引用类型 o Object o Array o Function • 声明变量时不同的内存地址分配&#xff1a; o 简单类型的…

德克萨斯大学奥斯汀分校自然语言处理硕士课程汉化版(第六周) - 预训练模型

预训练模型 1. 预训练模型介绍 1.1. ELMo1.2. GPT1.3. BERT 2. Seq2Seq 2.1. T52.2. BART 3. Tokenization 1. 预训练模型介绍 在预训练语言模型出现之前&#xff0c;统计语言模型&#xff08;如N-gram模型&#xff09;是主流方法。这些模型利用统计方法来预测文本中的下一个…

浙江大学蒋明凯研究员《Nature》正刊最新成果!揭示生态系统磷循环响应大气二氧化碳浓度升高关键机制

随着大气二氧化碳浓度的升高&#xff0c;陆地生态系统固存额外碳汇的能力取决于土壤养分的可利用性。前期的研究证据表明&#xff0c;在土壤低磷环境下&#xff0c;大气二氧化碳浓度的升高可以提升成熟森林的光合速率&#xff0c;但是没有产生额外生物量固碳。热带和亚热带森林…

自定义idea插件之hello idea plugin

写在前面 最近一直想研究下自定义idea插件的内容&#xff0c;这样如果是想要什么插件&#xff0c;但又一时找不到合适的&#xff0c;就可以自己来搞啦&#xff01;这不终于有时间来研究下&#xff0c;但过程可谓是一波三折&#xff0c;再一次切身体验了下万事开头难。那么&…

自然语言处理:第三十二章HippoRAG:性能提高20% - 受海马体启发的RAG

文章链接: HippoRAG: Neurobiologically Inspired Long-Term Memory for Large Language Models 项目地址: OSU-NLP-Group/HippoRAG: HippoRAG is a novel RAG framework inspired by human long-term memory that enables LLMs to continuously integrate knowledge across e…

macbook本地部署 pyhive环境连接 hive用例

前言 公司的测试和生产环境中尚未提供基于Hive的客户端。若希望尝试操作Hive表&#xff0c;目前一个可行的方案是使用Python语言&#xff0c;通过借助pyhive库&#xff0c;您可以对Hive表进行各种操作。以下是一些示例记录供您参考。 一、pyhive是什么&#xff1f; PyHive是一…

电脑提示缺失iUtils.dll文件怎么办,分享几种靠谱的解决方法

在现代科技发展迅猛的时代&#xff0c;电脑已经成为我们生活和工作中不可或缺的工具。然而&#xff0c;随着使用时间的增长&#xff0c;电脑也会出现一些问题&#xff0c;其中之一就是开机时弹出窗口提示找不到iUtils.dll文件。这个问题可能会给用户带来困扰和不便&#xff0c;…

[AIGC] Springboot 自动配置的作用及理由

在详细解释SpringBoot的自动配置之前&#xff0c;先介绍以下背景知识。在创建现代复杂的应用程序时&#xff0c;一个困难的部分是正确地设置您的开发环境。这个问题尤其在Java世界中尤为突出&#xff0c;因为您必须管理和配置许多独立的标准和技术。 当我们谈论Spring Boot的自…

react 基础样式的控制(行内和className)

import ./index.cssconst style{color:red,font-size:150px }function App() {return (<div className"App"><h1>行内样式控制</h1><h1 style{{color:red,font-size:150px}} >asd </h1><span style{style} >asd </span>&l…

二叉排序树--c++

【相关知识】 二叉排序树&#xff08;也称二叉查找树&#xff09;&#xff1a;或者是一棵空的二叉树&#xff0c;或者是具有下列性质的二叉树&#xff1a; ⑴ 若它的左子树不空&#xff0c;则左子树上所有结点的值均小于根结点的值&#xff1b; ⑵ 若它的右子树不空&#xff0c…

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

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

Java装饰器模式,装饰器模式通常通过创建一个接口和一个或多个实现了该接口的类来开始,然后创建装饰器类,这些类也实现了相同的接口

1、定义一个接口Component public interface Component { void operation(); }2、创建一个实现了Component接口的简单类SimpleComponent public class SimpleComponent implements Component { Override public void operation() { System.out.println("SimpleCom…

【JS重点知识05】正则表达式

本文章目标&#xff1a;学习正则表达式概念及语法&#xff0c;编写简单的正则表达式实现字符查找或检测&#xff1b; 一&#xff1a;正则表达式简介 1 什么是正则表达式 是用于匹配字符串中字符组合的模式。在JS中&#xff0c;正则表达式也是对象 2 正则表达式作用 表单验…

【C#】WinForm关闭新(二级)界面使主程序关闭

参考视频&#xff1a;https://www.bilibili.com/video/BV1JY4y1G7jo?p14&vd_source1c57ab1b2e551da5b65c0dfb0f05a493 1.背景介绍 主程序界面&#xff0c;点击弹出二级界面&#xff08;同时隐藏主界面&#xff09;&#xff0c;不做任何设置&#xff0c;这时关闭二级界面…

FM1202,FM020和利时备品

FM1202,FM020和利时备品,统硬件设备、数据库、控制算法、图形、报表&#xff09;和相关系统参数的设置。对整个系统进行监视和控制。操作员站主要完成以下FM1202,FM020和利时备品,各种监视信息的显示、查询和打印&#xff0c;主要有工艺流程图显示、趋势显示、参数列表显示、报…