【专栏】RPC系列(实战)-低配版NameServer

news/2024/5/19 0:08:21/文章来源:https://blog.csdn.net/scwMason/article/details/127138440

公众号【离心计划】,一起离开地球表面

【RPC系列合集】

【专栏】RPC系列(理论)-夜的第一章

【专栏】RPC系列(理论)-协议与序列化

【专栏】RPC系列(理论)-动态代理

【专栏】RPC系列(实战)-摸清RPC骨架

【专栏】RPC系列(实战)-优雅的序列化

【专栏】RPC系列(番外)-IO模型与线程模型

【专栏】RPC系列(番外)-“土气”的IO实现

【专栏】RPC系列(实战)-Hello Netty

| 前言

    上一小节我们用Netty实现了RPC的服务端与客户端通信,加上我们之前的序列化部分,已经可以成功进行发送请求到返回响应了。还记得我们在写Netty部分的代码时有一个RpcRequestHandler和RpcResponseHandler,针对每个请求或响应需要做对应的处理。在发请求前,客户端需要知道目标的地址和端口,这是发请求的必要条件;在处理请求时,我们要做的事情是根据请求中携带的函数签名,找到服务端的方法,然后传入携带的参数调用方法,拿到结果后包装成Response给客户端,所以被调用的服务需要注册在Netty服务端,也需要注册在NameServer中提供服务签名与地址的映射关系。

代码仓库:https://github.com/JAYqq/my-sparrow-rpc/tree/v1.3,一定要参考这边的完整代码

| NameServer

    我们在【专栏】RPC系列(实战)-摸清RPC骨架中提到过NameServer,在这里我再总结一下NameServer最重要的功能:

  1. 服务注册与发现。每个远程服务需要在NameServer注册好自己的身份信息与网络地址的对应关系,才能提供生产服务

  2. 路由策略与负载均衡。一个服务往往对应多台服务器,以集群的方式提供服务,那么以怎样的策略合理分配流量到这些机器是应对高并发的重要条件

  3. 健康检测。服务器往往会因为各种内外部原因停止服务,此时就不应有流量继续进来,因此通过健康检测机制去“保活”也很重要

    而我们在Sparrow-Rpc中没有去实现那么完美的NameServer,而仅仅支持了服务注册与发现,原因是我们可以使用现有的开源框架像Zookeeper为我们做这些事情,当然zk也有一些问题,像RocketMq就自己实现了NameServer机制,本系列教程中弱化了NameServer的开发工作,读者明白它的作用就行,有兴趣可以自行查阅。

    ok,首先我们在sparrow-rpc-api模块下创建一个NameServer接口,并定义了三个方法

public interface NameService {/*** 服务注册** @param serviceSign* @param uri*/void registerServer(String serviceSign, URI uri);/*** 服务下线*/void unregisterServer(String serviceSign, URI uri);/*** 服务查询*/URI seekService(String serviceSign);
}

    然后我们实现一个基于Json文件的NameServer,将服务与地址的关系记录持久化到Json中,并编写SPI的配置文件com.sparrow.rpc.api.NameService

com.sparrow.rpc.namesrv.impl.FileNameService
public class FileNameService implements NameService

    我们服务注册的方法用到了一个MetaInfo的元信息类,用来存储服务信息,整个方法逻辑很简单就是把服务信息组装成Json写入文件中

@Override
public void registerServer(String serviceSign, URI uri) {logger.info("Service {} register,uri:{}", serviceSign, uri.toString());MetaInfo metaInfo = new MetaInfo();metaInfo.setServiceSign(serviceSign);metaInfo.setUri(uri);String jsonMeta = JsonUtil.readJson(metaDataFile);try {JsonMetaInfo jsonMetaInfo;if (jsonMeta.isEmpty()) {jsonMetaInfo = new JsonMetaInfo();List<MetaInfo> metaInfos = new ArrayList<>();metaInfos.add(metaInfo);jsonMetaInfo.setMetaInfos(metaInfos);} else {jsonMetaInfo = JsonUtil.readValue(jsonMeta, JsonMetaInfo.class);jsonMetaInfo.getMetaInfos().add(metaInfo);}JsonUtil.writeJson(metaDataFile, jsonMetaInfo);} catch (Exception e) {throw new RuntimeException(e);}
}

     服务发现的方法也很简单,找到第一个与服务签名相同的信息就返回其元信息

@Override
public URI seekService(String serviceSign) {if (serviceSign.isEmpty()) {return null;}try {String jsonMeta = JsonUtil.readJson(metaDataFile);JsonMetaInfo jsonMetaInfo = JsonUtil.readValue(jsonMeta, JsonMetaInfo.class);for (MetaInfo metaInfo : jsonMetaInfo.getMetaInfos()) {if (metaInfo.getServiceSign().equals(serviceSign)) {return metaInfo.getUri();}}} catch (Exception e) {throw new RuntimeException(e);}return null;
}

| ServiceHub

    在前言中提到,除了在NameServer完成服务注册(UserService对应10.23.4.56),还需要在Netty服务端维护一个服务签名与具体服务类的对应关系(UserService与其对应的Class),它们之间的联系像这样

private Map<String, Object> serviceMp = new ConcurrentHashMap<>();

    然后为了将NameServer以及其他Rpc内部功能性方法与具体使用者解耦,我们创建一个RpcAccessor接口,把Rpc服务相关的方法收敛到一起

public interface RpcAccessor extends Closeable {/*** 获取NameServer** @return*/default NameService getNameService() {return SpiSupport.load(NameService.class);}/*** 注册RPC服务** @param serviceSign 服务签名* @param service     服务实例* @param clazz       服务接口类* @param <T>         类型* @return 服务地址*/<T> URI addRpcService(String serviceSign, T service, Class<T> clazz);/*** 启动RPC服务*/Closeable start() throws Exception;}

    然后在sparrow-rpc-core中创建一个实现类NettyRpcAccessor,并维护SPI的配置信息。其中addService就是与服务注册相关的部分

com.sparrow.rpc.core.NettyRpcAccessor
@Override
public <T> URI addRpcService(String serviceSign, T service, Class<T> clazz) {//服务实例注册ServiceHub.getInstance().addService(serviceSign, service);//服务URI注册NameService nameService = SpiSupport.load(NameService.class);URI localUri = getLocalUri();if (Objects.isNull(localUri)) {throw new IllegalStateException("Get local uri fail");}nameService.registerServer(serviceSign, localUri);return null;
}

| 小结

    这一节我们实现了一个低配版的NameServer,如果你想拥有一个工业级的服务注册中心,那么可以采用开源的工具帮助你,你也可以参考开源框架自己实现的注册机制,至少通过这一小节我们知道了NameServer的功能,下一小节我们会讲解动态代理部分的实现,把整个调用链路上最后的代理类去补上。

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

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

相关文章

读书笔记:软件工程(4) - 软件过程模型:瀑布模型

软件过程模型 为了改变软件开发的混乱状况&#xff0c;使软件开发更加有序。 瀑布模型 又称为经典生命周期&#xff0c;它提出了一个系统的&#xff0c;顺序的软件开发方法&#xff0c;从用户需求规格说明开始&#xff0c;通过策划&#xff0c;建模&#xff0c;构建和部署的…

Easyx基本使用(三)

Easyx基本使用&#xff08;三&#xff09; ——绘制简单图形 1. 绘制点&#xff08;putpixel&#xff09; void putpixel(int x,int y,COLORREF color );x&#xff1a;点的x坐标y&#xff1a;点的y坐标color&#xff1a;点的颜色返回值&#xff1a;无 #include <easyx.h…

程序员的数学课15 递归:如何计算汉诺塔问题的移动步数?

递归是重要的程序开发思想&#xff0c;比如程序源代码缩进、树形数据结构、XML 语法、快速排序法等都有递归的影子。 那么&#xff0c;递归思维的本质到底是什么呢&#xff1f;递归的理念看似隐讳&#xff0c;实则非常清晰明了。 为了让你由浅入深地理解它&#xff0c;这一讲…

paddle 训练模型的保持和载入

模型保持中的常见问题 &#xff0c;查看官网 模型保存常见问题-使用文档-PaddlePaddle深度学习平台 一、保存载入体系简介 模型保存与载入 一、保存载入体系简介 1.1 基础API保存载入体系 飞桨框架2.1对模型与参数的保存与载入相关接口进行了梳理&#xff1a;对于训练调优场景…

2023秋招nlp笔试题-太初

单选题 浮点数的表示范围和精度取决于 浮点数的取值范围由阶码的位数决定,而浮点数的精度由尾数的位数决定 响应中断请求的条件是 A.外设提出中断; B.外设工作完成和系统允许时; C.外设工作完成和中断标记触发器为“1”时; D.CPU提出中断。 计算器浮点运算速度为85.0167PF…

Day30_路由的props配置

提出问题&#xff1a; Detail是如何得到别传给他的参数&#xff0c;以及如何简化写法。 第一种写法的props 对象式 使用的很少&#xff0c;传递的是死数据 1.index.js组件里面 2.在detail组件里面 3效果图&#xff1a; 第二种写法props 布尔值 仅限于params参数传递…

心理学考研难度 分析

全国招收心理学院校排名 高等教育评价专业机构软科发布了“2020软科中国最好学科排名”&#xff0c;心理学院校有55所上榜&#xff0c;排名前10%依次为北京师范大学、北京大学、华南师范大学、西南大学、华东师范大学、华中师范大学、上海交通大学、浙江大学、陕西师范大学、上…

内存对齐对性能的影响

计算字节数 在 Go 语言中&#xff0c;可以使用 unsafe.Sizeof 计算出一个数据类型实例需要占用的字节数。 package mainimport ("fmt""unsafe" )type Args struct {num1 intnum2 int }type Flag struct {num1 int16num2 int32 }func main() {fmt.Println…

浅析axios原理与使用(包括axios的优雅写法与res的解构赋值)

目录前言一&#xff0c;什么是axios二&#xff0c;axios的基本使用2.1 不含参的axios调用2.2 含参数的axios调用三&#xff0c;axios的原理&#xff08;拦截器&#xff09;3.1 客户端与服务器之间的交互原理&#xff08;复习可略过&#xff09;3.2 浅析axios原理四&#xff0c;…

西瓜书研读——第三章 线性模型: 线性判别分析 LDA

西瓜书研读系列&#xff1a; 西瓜书研读——第三章 线性模型&#xff1a;一元线性回归 西瓜书研读——第三章 线性模型&#xff1a;多元线性回归 西瓜书研读——第三章 线性模型&#xff1a;线性几率回归&#xff08;逻辑回归&#xff09; 主要教材为西瓜书&#xff0c;结合…

K8s存储与GlusterFS实战

一、存储概述 1、共享存储机制概述 Kubernetes对于有状态的容器应用或者对数据需要持久化的应用&#xff0c;不仅需要将容器内的目录挂载到宿主机的目录或者emptyDir临时存储卷&#xff0c;而且需要更加可靠的存储来保存应用产生的重要数据&#xff0c;以便容器应用在重建之后…

常见的垃圾回收机制

如何工作在某些 Java 虚拟机中,堆的实现截然不同:它更像一个传送带,每分配一个新对象,它就向前移动一格。 这意味着对象存储空间的分配速度特别快。Java 的"堆指针"只是简单地移动到尚未分配的区域,所以它的效率与 C++ 在栈上分配空间的效率相当垃圾回收器工作时…

将Springbooot项目部署到云服务器上运行

将Springbooot项目部署到云服务器上运行一、项目打包二、将结果jar文件上传服务器三、授予jar文件权限&#xff0c;并运行四、运行失败可能原因一、项目打包 File->Project Structure Project Settings -> Artifacts -> 点击 号 -> JAR -> From modules with d…

Linux-常见命令(三)

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接收失败&#xff0c;但我不能接收放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正。 目录 find指令&…

2022-2023-1 20221318 《计算机基础和程序设计》第五周学习总结

作业信息 这个作业属于那个班级 https://edu.cnblogs.com/campus/besti/2022-2023-1-CFAP 作业要求 https://www.cnblogs.com/rocedu/p/9577842.html#WEEK03 作业目标 学习《计算机科学概论》的第6章 《程序设计》第4章 作业正文 https://i.cnblogs.com/posts/edit;postId=1673…

JavaScript:DOM

目录 一、DOM介绍 1、节点和节点的关系 2、获取页面中的元素可以使用以下几种方式 3、鼠标点击事件 4、访问节点 5、获取元素的样式 一、DOM介绍 DOM&#xff1a;Document Object Model&#xff08;文档对象模型&#xff09; DOM是W3C组织推荐的处理可扩展标记语言的标…

数据结构线性表

目录 2.1 线性表的基本概念 2.2 线性表的顺序表示 2.3 线性表的链式表示 2.3.1 单链表 2.3.2 循环单链表 2.3.3 双向链表 2.3.4 双向循环链表 2.3.5 静态链表 牢固掌握线性表在两种存储结构下的各种基本操作&#xff0c;牢记各种算法思想 2.1 线性表的基本概念 Def&a…

Linux 学习 -- 容器技术

Linux 学习 – 容器技术 容器基础概述容器部署 一、容器基础概述 容器(Container) : 定义&#xff1a;指的是针对应用所需的运行环境资源&#xff08;依赖库/目录/网络/用户……等&#xff09;进行整体封装的技术。 特点&#xff1a;封装好的镜像相比虚拟机的粒度要更细&…

类与对象(十八)----super关键字

super关键字 super代表父类的引用&#xff0c;可以用于访问父类的属性&#xff0c;方法&#xff0c;构造器。 super使用 使用super访问父类的属性&#xff1a;权限内的才可以访问。使用语法----super.属性名 public class A {public int n1 12;protected int n2 12;int n…

程序员的数学课10 信息熵:事件的不确定性如何计算?

你好&#xff0c;欢迎来到第 10 课时——信息熵&#xff1a;事件的不确定性如何计算&#xff1f; 从加乘法则开始&#xff0c;我们基于事情的不确定性出发&#xff0c;尝试计算事情发生的可能性。然而&#xff0c;对于事件与事件之间的不确定性如何相比和衡量&#xff0c;单独…