Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Glusterfs之rpc模块源码分析(中)之Glusterfs的rpc模块实现(2)

Glusterfs之rpc模块源码分析(中)之Glusterfs的rpc模块实现(2)

作者头像
技巅
发布于 2018-05-25 05:55:43
发布于 2018-05-25 05:55:43
8060
举报
文章被收录于专栏:技巅技巅
第二节、rpc客户端实现原理及代码分析

rpc客户端主要发起一个rpc请求,执行完rpc请求以后就退出rpc,下面分析客户端rpc请求建立的整个过程。Rpc客户端请求建立的第一步是执行cli_rpc_init函数,主要实现代码如下:

        this = THIS;//取得本线程的xlator列表

        cli_rpc_prog = &cli_prog;//设置rpc调用过程集合(许多函数)

        options = dict_new ();//新建一个字典数据结构用于存放选项信息

        ret = dict_set_str (options, “remote-host”, state->remote_host);//设置host

        if (state->remote_port)

                port = state->remote_port;

        ret = dict_set_int32 (options, “remote-port”, port);//设置端口号

        ret = dict_set_str (options, “transport.address-family”, “inet”);//设置协议族为inet

        rpc = rpc_clnt_new (options, this->ctx, this->name);//新建一个rpc客户端对象

        ret = rpc_clnt_register_notify (rpc, cli_rpc_notify, this);//注册rpc请求通知函数

        rpc_clnt_start (rpc);//开始rpc

这段代码其实是glusterfs客户端程序启动时建立rpc请求的初始化过程函数,真正独立开始建立一个rpc请求的过程是从函数rpc_clnt_new开始的,下面就分析这个函数的功能,先看主要代码:

        rpc = GF_CALLOC (1, sizeof (*rpc), gf_common_mt_rpcclnt_t);//为客户端rpc对象分配内存

        pthread_mutex_init (&rpc->lock, NULL);//初始化锁

        rpc->ctx = ctx;//属于哪一个ctx

//新建请求内存池

        rpc->reqpool = mem_pool_new (struct rpc_req, RPC_CLNT_DEFAULT_REQUEST_COUNT);

        //保存帧数据的内存池

rpc->saved_frames_pool = mem_pool_new (struct saved_frame,

                                              RPC_CLNT_DEFAULT_REQUEST_COUNT);

        ret = rpc_clnt_connection_init (rpc, ctx, options, name);//初始化rpc请求连接

        rpc = rpc_clnt_ref (rpc);//客户端对象引用计数加1

        INIT_LIST_HEAD (&rpc->programs);//初始化程序集的链表

rpc客户端发送请求也需要装载相应的协议库,它主要使用协议库里面的初始化函数和链接函数,上面代码中rpc_clnt_connection_init函数主要完成协议库装载功能和链接选项的一些初始化功能,具体实现的主要代码如下:

  pthread_mutex_init (&clnt->conn.lock, NULL);//初始化锁

conn->trans = rpc_transport_load (ctx, options, name);//装载协议库并执行初始化init

        rpc_transport_ref (conn->trans);//增加传输层的引用计数

        conn->rpc_clnt = clnt;//连接对象属于哪一个客户端对象

        ret = rpc_transport_register_notify (conn->trans, rpc_clnt_notify, conn);//注册通知函数

conn->saved_frames = saved_frames_new ();//新建一个保存帧数据的对象

通过上面代码执行以后基本的初始化工作已经完毕,下一步就是建立于rpc服务的链接,此功能在函数rpc_clnt_start中实现,也就是在所有初始化工作完成以后调用此函数发起链接,主要代码如下:

rpc_clnt_reconnect (conn->trans);

继续跟踪函数rpc_clnt_reconnect:

        pthread_mutex_lock (&conn->lock);//初始化锁

        {

                if (conn->reconnect)//如果重新链接正在进行

                        gf_timer_call_cancel (clnt->ctx, conn->reconnect);//取消正在的链接

                conn->reconnect = 0;//初始化为0

                if (conn->connected == 0) {//还没有完成链接

                        tv.tv_sec = 3;//时间三秒

//发起传输层的链接

                        ret = rpc_transport_connect (trans, conn->config.remote_port);

//设置重链接对象

                        conn->reconnect = gf_timer_call_after (clnt->ctx, tv,

                                                     rpc_clnt_reconnect, trans);

                } 

        }

        pthread_mutex_unlock (&conn->lock);//解锁

        if ((ret == -1) && (errno != EINPROGRESS) && (clnt->notifyfn)) {

//建立链接失败就通知rpc客户端对象(调用通知函数)

                clnt->notifyfn (clnt, clnt->mydata, RPC_CLNT_DISCONNECT, NULL);

        }

真正的链接是在具体的协议中的链接函数中执行,下面以tcp为例,看看它的链接函数的实现,主要代码如下:

//得到客户端协议族相关信息

        ret = socket_client_get_remote_sockaddr (this, SA (&sockaddr), &sockaddr_len, &sa_family)

        if (port > 0)

                ((struct sockaddr_in *) (&sockaddr))->sin_port = htons (port);//端口字节序转换

        pthread_mutex_lock (&priv->lock);

        {

                memcpy (&this->peerinfo.sockaddr, &sockaddr, sockaddr_len);//赋值sockaddr信息

                this->peerinfo.sockaddr_len = sockaddr_len;//地址长度保存

                priv->sock = socket (sa_family, SOCK_STREAM, 0);//创建socket

                setsockopt (priv->sock, SOL_SOCKET, SO_RCVBUF, &priv->windowsize,

                                sizeof (priv->windowsize)) < 0) ;//设置接收的系统缓冲区

                setsockopt (priv->sock, SOL_SOCKET, SO_SNDBUF, &priv->windowsize,

                                sizeof (priv->windowsize)) < 0);//发送缓冲区设置

                if (priv->nodelay) {

                        ret = __socket_nodelay (priv->sock);//设置是否延迟发送(等待一个完整的)

                 }

                if (!priv->bio) {

                        ret = __socket_nonblock (priv->sock);//设置非阻塞

                }

                if (priv->keepalive) {

                        ret = __socket_keepalive (priv->sock, priv->keepaliveintvl,

                                                  priv->keepaliveidle);//保存链接活动

                }

                SA (&this->myinfo.sockaddr)->sa_family =

                        SA (&this->peerinfo.sockaddr)->sa_family;//保存协议族信息

                ret = client_bind (this, SA (&this->myinfo.sockaddr),//根据协议族做适当的绑定

                                   &this->myinfo.sockaddr_len, priv->sock);

                ret = connect (priv->sock, SA (&this->peerinfo.sockaddr),//发起链接请求

                               this->peerinfo.sockaddr_len);

priv->connected = 0;

                rpc_transport_ref (this);//传输对象引用加1

                priv->idx = event_register (ctx->event_pool, priv->sock,//注册epoll读写事件

                                            socket_event_handler, this, 1, 1);

        }

        pthread_mutex_unlock (&priv->lock);//解锁

整个链接过程的建立需要注意一点的就是会根据协议族的类型做适当的绑定,当发起链接以后就开始注册各种读写事件。处理这些事件发生的函数是socket_event_handler,主要代码如下:

  THIS = this->xl;//取得xlator

        priv = this->private;//取得私有数据

        pthread_mutex_lock (&priv->lock);//加锁

        {

                priv->idx = idx;//取得索引下表(对应的socket)

        }

        pthread_mutex_unlock (&priv->lock);

        if (!priv->connected) {//如果链接还没有建立先完成链接的建立

                ret = socket_connect_finish (this);//完成链接建立

        }

        if (!ret && poll_out) {//可写事件处理

                ret = socket_event_poll_out (this);

        }

        if (!ret && poll_in) {//可读事件处理

                ret = socket_event_poll_in (this);

        }

到此客户端rpc请求过程完全建立,当真正的发送一个rpc请求的时候就会响应相应的epoll的写事件,把包装好的数据帧发送到rpc服务器端,rpc客户端也会通过可读事件来接收rpc服务器端的响应信息。

总结:同rpc服务器端一眼,rpc客户端的建立也是比较复杂的过程,还是通过流程图加描述来展示一下整个rpc客户端的初始化过程,图如下:

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017年6月14日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Glusterfs之rpc模块源码分析(中)之Glusterfs的rpc模块实现(3)
第三节、rpc通信过程分析 前面两个小节分别对rpc服务端和客户端的建立流程做了详细的分析,也就是说rpc客户端和服务器端已经能够进行正常的通信了(rpc客户端已经通过connect链接上rpc服务器了),那么这一小节主要根据一个实际的例子来分析一个完整的rpc通信过程。 下面以客户端创建逻辑卷(volume)为例来分析rpc的通信过程,就以下面这个客户端的命令开始: gluster volume create test-volume server3:/exp3 server4:/e
技巅
2018/05/25
1.2K0
​fixed storoge.reserve检测磁盘预留空闲时间间隔问题
问题 当gluster/positrator中的当前运行时存储区的一个参数b 有多个参数b 时,在这个区域,当这个参数brick 的时候,在一个参数集的时间间隔内,在任何时间间隔的时间里,会出现提示“没有剩余空间”。这种情况可以很好地规避当存储集群要容满的时候进行扩容,而不是一直写到写满引发的集群不可用。 目前,这个服务区操作的车辆设置是storagetorage。配置,而变量是在一个线程内更新的使用这种情况,是一个硬代码默认方式,默认是 5s,如果这个时间间隔时间比较长,间隔时间高的情况是合适的情况,可以
用户4700054
2022/08/17
3190
【网络编程】十八、Reactor模式
​ 针对 阻塞 I/O 执行的系统调用可能因为无法立即完成而被操作系统挂起,直到等待的事件发生为止。比如,客户端通过 connect 向服务器发起连接时,connect 将首先发送同步报文段给服务器,然后等待服务器返回确认报文段。如果服务器的确认报文段没有立即到达客户端,则 connect 调用将被挂起,直到客户端收到确认报文段并唤醒 connect 调用。socket 的基础 API 中,可能被阻塞的系统调用包括 accept、send、recv 和 connect。
利刃大大
2025/05/29
810
【网络编程】十八、Reactor模式
【网络编程】三、TCP网络套接字编程
​ 可见这是比 udp 通信要复杂的,我们不仅仅要搞清楚如何进行通信,还要搞清楚通信的本质以及原理!这里我们先来解决如何搭建通信的环境!
利刃大大
2025/05/09
1040
【网络编程】三、TCP网络套接字编程
Glusterfs之rpc模块源码分析(中)之Glusterfs的rpc模块实现(1)
二、Glusterfs的rpc模块实现 第一节、rpc服务器端实现原理及代码分析 1.rpc服务初始化 Rpc服务的初始化工作在函数rpcsvc_init中实现的,实现代码如下: rpcsvc_t * rpcsvc_init (glusterfs_ctx_t *ctx, dict_t *options) { rpcsvc_t          *svc              = NULL;//所有rpc服务的全局状态描述对象 int               
技巅
2018/05/25
1.3K0
Glusterfs之nfs模块源码分析(中)之Glusterfs实现NFS服务器
五、Glusterfs实现NFS服务器 第一节、启动过程分析 Glusterfs的nfs服务器启动命令如下:  /usr/local/sbin/glusterfs -f /etc/glusterd/nfs/nfs-server.vol -p /etc/glusterd/nfs/run/nfs.pid   -l /usr/local/var/log/glusterfs/nfs.log 说明:所有列出的代码都把错误处理、参数检查和日志输出去掉了! 上面的命令会启动gluster
技巅
2018/05/25
2K0
glusterfs客户端挂载流程
Glusterfs 基本原理 Glusterfs 是基于fuse的分布式存储,功能上支持分布式/3副本/EC三种存储方式。Glusterfs采用堆栈式的架构设计,服务端和客户端采用translator. GlusterFS概念中,由一系列translator构成的完整功能栈称之为Volume,分配给一个volume的本地文件系统称为brick,被至少一个translator处理过的brick称为subvolume。客户端是由于volume类型来加载对应的translator,服务端也是一样,根据不同的volume的类型加载translator。客户端(glusterfs)通过挂载时候提供节点IP地址,来对应节点的服务端管理进程通信,获取brick源信息、客户端需要加载的配置,客户端根据配置初始化xlator,后续IO的流程按照xlator的顺序经过每个xlator的fop函数,然后直接和对应的glusterfsd的进程交互IO操作。glusterfsd也是一样,根据服务端配置文件,初始化服务端需要加载xlator,进行每个xlator的fop的操作,最终执行系统IO函数进行IO操作。节点的管理服务(glusterd),仅仅加载一个管理的xlator,处理来自glusterfs/gluster的请求,不会处理对应的IO操作操作。
用户4700054
2022/08/17
2.1K0
glusterfs客户端挂载流程
gluster quota介绍
gluster quta命令 // 开启volume的配额功能 # gluster volume quota {test-volume} enable // 设置hard limit,hard是基于容量来限制目录使用限制/可以设置大小的单位使MB/GB/TB quota #PB # gluster volume {test-volume} limit-usage /data1 10GB quota {test-volume} 限制使用 /data1 1TB // 设置软限制,软限制是指定使用或者可以使用硬限制的说明的属性。当使用了软限制设置的目录目录限制时,用户仍然可以继续写入,但是会不断地写 //如果用户的写入/data1目录设置硬限制为1T,如果数据达到1T会报错;如果没有达到1T,达到1T * 0.5,会在该处写的brick日志已经不断的日志,来通知在达到soft limit # gluster volume quota {test-volume} limit-usage /data1 1T 50% // 显示配额限制的列表 # gluster volume quota {test-volume} 列表 // 设置soft-limit的检查的间隔时间,单位是秒。默认是60s,最大是 1800s gluster volume quota {test-volume} soft-timeout 100s // 设置 hard-limit 的检查的间隔时间,单位是秒 默认是 5s,最大是 60s gluster volume quota {test-volume} hard-timeout 100s // 设置根对象为目录下的对象3个对象 glust test1 limit-quotaobjects /data3 3 // 当客户端挂载时使用 -h 查看容量,此设置开启硬性 配额设置用户在目录时的容量上# gluster volume} features.quota-deem
用户4700054
2022/08/17
1.3K0
gluster quota介绍
golang网络框架netpoll(Multi-Reactor模型)核心源码分析
netpoll是字节不久前开源的一款golang编写的高性能网络框架(基于Multi-Reactor模型),旨在用于处理rpc场景,详细的介绍可参见下图介绍。
jaydenwen123
2022/03/30
4.1K1
golang网络框架netpoll(Multi-Reactor模型)核心源码分析
大厨小鲜——基于Netty自己动手编写RPC框架
今天我们要来做一道小菜,这道菜就是RPC通讯框架。它使用netty作为原料,fastjson序列化工具作为调料,来实现一个极简的多线程RPC服务框架。
老钱
2018/08/14
7110
深入理解 RPC 交互流程
文节我们讲解 RPC 的消息交互流程,目的是搞清楚一个简单的 RPC 方法调用背后究竟发生了怎样复杂曲折的故事,以看透 RPC 的本质。
老钱
2018/08/14
9490
c++ 网络编程(九)TCP/IP LINUX/windows--使用IOCP模型 多线程超详细教程 以及 多线程实现服务端
原文链接:https://www.cnblogs.com/DOMLX/p/9661012.html
徐飞机
2018/09/30
3.3K0
c++ 网络编程(九)TCP/IP LINUX/windows--使用IOCP模型      多线程超详细教程 以及 多线程实现服务端
Glusterfs Xlator开发详解
读写操作会打印出当前xlator的名称和下一个xlator的名称,同时会在日志中打印操作类型,inod,gfid的信息
用户4700054
2022/08/17
7980
Glusterfs Xlator开发详解
简明linux系统编程--互斥锁--TCP--UDP初识
我们的这个互斥锁,分为上锁和解锁,我们的某一个进程占用这个资源的时候,就会把这个共享区域上锁,表示这个空间资源已经被使用,其他的想要使用这个资源的进程就会被挂起,直到我们的这个正在使用资源的进程使用完毕,其他的被挂起的进程才可以使用这个资源,这个资源就会被从原来的上锁状态到现在的解锁状态,被其他的进程使用;
阑梦清川
2025/02/24
1090
简明linux系统编程--互斥锁--TCP--UDP初识
Linux源码分析-RDMA的通信连接管理CM模块
RDMA CM 是一种通信管理器,用于设置可靠、连接和不可靠的数据报数据传输。 它提供用于建立连接的 RDMA 传输中立接口。 API 概念基于套接字,但适用于基于队列对 (QP) 的语义:通信必须通过特定的 RDMA 设备进行,并且数据传输基于消息。 RDMA CM 可以控制 RDMA API 的 QP 和通信管理(连接建立/拆除)部分,或者仅控制通信管理部分。 它与 libibverbs 库定义的 verbs API 结合使用。 libibverbs 库提供了发送和接收数据所需的底层接口。 RDMA CM 可以异步或同步操作。 用户通过在特定调用中使用 rdma_cm 事件通道参数来控制操作模式。 如果提供了事件通道,rdma_cm 标识符将报告该通道上的事件数据(例如连接结果)。 如果未提供通道,则所选 rdma_cm 标识符的所有 rdma_cm 操作将被阻止,直到完成。 RDMA CM 为不同的 libibverbs 提供商提供了一个选项来宣传和使用特定于该提供商的各种 QP 配置选项。 此功能称为 ECE(增强连接建立)
晓兵
2024/04/27
1.2K0
Linux源码分析-RDMA的通信连接管理CM模块
Swoole 源码分析之 WebSocket 模块
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。它允许客户端和服务器之间进行实时数据传输。
码农先森
2024/06/26
1280
【c++】测试用例:C/S模型、epoll模型
已经好几次去网上找现成的改一下用了,那我还不如自己备一份儿。 文章目录 Server Client epoll Server #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> int main()
看、未来
2022/01/10
5320
python与rpc服务
随着企业 IT 服务的不断发展,单台服务器逐渐无法承受用户日益增长的请求压力时,就需要多台服务器联合起来构成「服务集群」共同对外提供服务。
超蛋lhy
2019/09/23
1.6K0
python与rpc服务
Memcached启动分析
在文本协议的memcached中,我们nc/telent后输入stats命令,会很快地输出一些当前memcached的信息的。这些就是stats信息。并不是输入stats的时候才遍历统计出来的。而是已经保存好了这份信息。代码调用在main函数中的:
tunsuy
2022/10/27
6570
TarsRPC源码解读篇:使用C++重写Tars-RPC主逻辑框架
Tars(https://github.com/TarsCloud/Tars) 是腾讯开源的一套微服务框架。其基础是Tars RPC。对于有一些基础的同学来说,直接看RPC源码无疑是了解Tars的最佳途径。
路小饭
2019/01/07
5.8K0
TarsRPC源码解读篇:使用C++重写Tars-RPC主逻辑框架
推荐阅读
相关推荐
Glusterfs之rpc模块源码分析(中)之Glusterfs的rpc模块实现(3)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档