go语言rpc超时时间 go语言rpc框架

nginx容灾配置,rpc重试机制,upstream配置proxy_next_upstream_timeout 和 proxy_xx_timeout的关系是怎样的?
proxy_xx_time 都是针对upstream里面一个节点的超时时间,请求一个节点可以看做是一个原子操作,这个原子操作无论怎样都会按照自己的proxy_xx_timeout完成一次请求 , 在所有proxy_xx_timeout结束后依然得不到结果,这一次请求就超时了结束了
这次原子请求结束后,再来根据proxy_next_upstream_timeout来决定是否可以重试到下一个节点,如果原子请求结束后,用时还没到达proxy_next_upstream_timeout设置的时间 , 并且那么可以继续重试原子请求 。
重试多少次?直到重试到proxy_next_upstream_timeout设置的时间 或者 proxy_next_uptream_tries 次数,只要有一个条件满足 , 重试就停止 。意味着:在proxy_next_upstream_timeout时间内可以重试proxy_next_uptream_tries 次 。
如果在一个原子请求里,proxy_xx_timeout结束之前,proxy_next_upstream_timeout的时间更早结束 , 也就是说proxy_xx_timeout的时间总和proxy_next_upstream_timeout,那么当原子请求完,就可以直接结束,不用重试了 , 因为早已超过了proxy_next_upstream_timeout 的超时时间 。
这里一个很重要的概念是:proxy_xx_timeout是一个节点请求的原子超时时间,进去这原子请求里面,就无视外面的proxy_next_upstream_timeout 时间 。怎么也得这原子请求结束了再来看是否能重试
假设现在upstream有两个节点,一个8100,一个8200 , 并且这两个端口都不会应答,都会超时
分析:原子请求需要花费10秒,10秒出来,proxy_next_upstream_timeout的5秒已经过去,不会再重试,此时access_log看到请求10秒后返回504timeout
分析:第一个原子请求8100花费2秒,出来后看到proxy_next_upstream_timeout=5,此时还剩下3秒,可以再重试,到了下一个节点8200 , 8200也会超时,此时proxy_next_upstream_timeout剩下1秒,还可以重试,但是没有节点了,此时access_log看到请求 4秒 后返回504timeout 。注意这里不会等待5秒再超时 。没有节点了就直接返回504
上面的讨论都是针对节点超时来看是否能重试节点 , 当然实际上可能会因为节点5xx,4xx来决定重试,
proxy_next_upstream error timeout http_502 http_504; 这个配置就是决定什么情况需要进行重试的,这里配置的就是超时、502、504、异常返回 。这种不是极端超时的情况,一般都能proxy_next_upstream_timeout在这超时范围内重试
nginx的这个proxy_next_upstream_timeout 是rpc微服务容灾的核心配置,这个配置简单但非常重要,决定着服务在异常的时候能否重试到别的节点,如果配置不当不能重试,那就会影响用户体验 。
四、HBase客户端1、通过Configuration初始化集群Connection
1.1、Connction维持了客户端到整个HBase集群的链接go语言rpc超时时间,如一个HBase集群有2个Master、5个RegionServer,那么一般来说整个Connection会维持一个到Active Master的TCP连接和5个到ReginonServer的TCP链接 。
1.2、通常一个进程只需要为一个独立的集群建立一个Connection即可,并不需要建立连接池 。
1.3、Connection还缓存了访问的Meta信息,后续的大部分请求都可以通过缓存的Meta信息定位到对应的Region Server 。
2、通过Connection初始化Table
2.1、Table是一个非常轻量级的对象 , 它所使用的连接资源、配置信息、线程池、Meta缓存等都来自于Connection 。
2.2、由同一个Connection创建的多个Table,都会共享连接、配置信息、线程池、Meta缓存这些资源
2.3、在branch-1以及之前的版本中,Table并不是线程安全的类 , 所以不建议在多个线程中使用同一个Table实例 。在HBase 2.00及之后,Table已经实现了线程安全 。
2.4、由于Table是一个非常轻量级的对象,所以可以通过Connection为每个请求创建一个Table,但是记?。?在该请求执行完毕之后需要关闭Table资源 。
3、hbase:meta
3.1、hbase:meta用来保存整个集群的region信息
3.2、hbase:meta在HBase中保证始终只有一个Region,这是为了确保meta表多次操作的原子性,因为HBase本质上只支持Region级别的事务所谓Region级别的事务是指:当多个操作落在同一个Region内时 , HBase能保证这一批操作执行的原子性 。如果多个操作分散在不同的Region,则无法保证这批操作的原子性
3.3、hbase:meta的一行就对应一个Region. 它的rowkey主要由TableName、StartRow、TimeStamp、EncodeName、标识这个Region是属于哪个表,表Rowkey的起始行以及Region的创建时间戳
3.4、hbase:meta只有一个列簇infogo语言rpc超时时间他有4列,info:regioninfo、info:seqnumDuringOpen、info:server、info:serverstartcode,表示这个表rowkey的起始位置,region落在哪个RegionServer上以及所在RegionServer的启动时间戳
4、HBase超时参数设置
hbase.rpc.timeout:表示单次RPC请求的超时时间,默认是60 000ms 。
hbase.clinet.retries.number:最多允许发生多少次RPC重试操作默认是35次 。
hbase.clinet.pause:表示连续两次RPC重试之间的休眠时间,默认是100ms 。重试休眠时间是按照随机退避算法设计的 。也就是重试次数越多,休眠间隔时间就会越来越长 。按照默认的重试次数35 , 则可能长期卡在休眠和重试两个步骤中
hbase.clinet.operation.timeout:表示单次API的超时时间,默认值为1 200 000ms.一次API可能会有多次RPC重试,这个参数是API操作的总超时 。
5、CAScheckAndPut、inCrementColumnValue操作是Region级别串行执行的,吞吐受限,在HBase 2.x版本已调整设计,对于同一个Region内部的不同行可以并行执行CAS,这样大大提交了Region内部的CAS吞吐
6、Filter使用避坑指南
6.1、PrefixFilter 前缀过滤
低效使用方式:
Scan scan = new Scan();
scan.setFilter(new PrefixFillter(Bytes.toBytes("def")));
这个Scan虽然能得到预期的效果 , 但是并不高效,因为对于rowKey在区间(-∞,def)的数据,会一条条扫描 , 发现前缀不为def,就读下一行 , 直到找到第一个rowkey为def的行为止
高效使用方式:
Scan scan = new Scan();
scan.setStartRow(Bytes.toBytes("def"));
scan.setFilter(new PrefixFillter(Bytes.toBytes("def")));
增加了一个startRow 。RegionServer发现Scan设置了startRow,首先会寻址定位到startRow 。这样就跳过了大量的(-∞,def)的数据 。
最高效的使用方式:
Scan scan = new Scan();
scan.setStartRow(Bytes.toBytes("def"));
scan.setStopRow(Bytes.toBytes("deg"));
将PrefixFilter直接展开,扫描[def,deg)区间的数据,这样效率是最高的 。
6.2、PageFilter:表有5个Region起始key为(-∞,1)、[1,2)、[2,3)、[3,4)、[4, ∞)每个Region 都有超过100条数据
错误的使用方式:
Scan scan = new Scan();
scan.setStartRow(Bytes.toBytes("1"));
scan.setStopRow(Bytes.toBytes("3"));
scan.setFilter(new PageFilter(100))
这样写得出来的分页每页数据就会有200 条 。但是明明设置了分页每页条数是100 。原因是,它需要scan 2个Region.scan从一个region切换到另一个region之前的那个Filter的内部状态就无效了,新的region内部用的是一个全新的Filter.Filter计数器被清零 。Filter不是全局的,所以它分别从2个region各查了100 条 , 总共200 条返回 。
正确的使用方式:
如果想实现分页功能,可以不通过Filter而直接通过limit来实现 。
Scan scan = new Scan();
scan.setStartRow(Bytes.toBytes("1"));
scan.setStopRow(Bytes.toBytes("3"));
scan.setLimit(100);
所以对于用户来说,正常情况下PageFilter并没有太多的存在价值
6.3、SingleColumnValueFilter
使用方式:
Scan scan = new Scan();
SingleColumnValueFilter scvf = newSingleColumnValueFilter(Bytes.toBytes("family"),Bytes.toBytes("qualifier"),
CompareOp.EQUAL,Bytes.toBytes("value"));
scan.setFilter(scvf);
表面上是将列簇为family,列为qualifier,值为value的cell返回给用户,但事实上那些不包含family:qualifier的行也会默认返回给用户 , 如果用户不希望读取那些不包含family:qualifier的数据,需要设计如下scan
Scan scan = new Scan();
SingleColumnValueFilter scvf = newSingleColumnValueFilter(Bytes.toBytes("family"),Bytes.toBytes("qualifier"),
CompareOp.EQUAL,Bytes.toBytes("value"));
scvf.setFiterIfMisssing(true);
scan.setFilter(scvf);
另外当SingleColumnValueFilter设置为filterIfMisssing为true时,和其他Filter组合成FilterList时可能导致返回的结果不正确 。建议是不要使用SingleColumnValueFilter与其他Filter组合成FilterList 。直接指定列 , 通过ValueFilter替换掉SingleColumnValueFilter
Scan scan = new Scan();
ValueFilter vf = new ValueFilter(CompareOf.EQUAL,new BinaryComparatoe(Bytes.toBytes("value")));
scan.addColum(Bytes.toBytes("family"),Bytes.toBytes("qualifier"));
scan.setFilter(vf);
7、HBase写入方式对比
7.1、table.put(Put):
每次执行都会执行一次RPC和磁盘持久化,写入吞吐受限于磁盘带宽、网络带宽,不会有数据丢失能保证put操作的原子性 。
7.2、table.put(ListPut):
客户端打包一批put提交,执行一次RPC,一次WAL 。相比第一种省略了多次往返的RPC和磁盘持久化 。但是时间会变长 。如果打包的put分布在多个Region 。则不能保证这一批put的原子性,应为HBase不支持跨Region的多行事务 , 失败的put会经历若干次重试 。
7.3、bulk load:
将待写入的数据生成HFile,然后采用bulk load方式将HFile直接加载到对于的Region的CF内 。这是一种完全离线的快速写入方式 。它应该是最快的批量写入手段,同时不会对线上的集群产生巨大压力,在load完HFile之后 , CF内部会进行Compaction,但是Compaction是异步的且可以限速,所以bulk load对线上集群非常友好 。
使用场景举例:
7.3.1、两个集群互为主备,其中一个集群由存在数据丢失,想通过另一备份集群的数据来修复异常集群 。最快的方式是:把备份集群的数据导一个快照拷贝到异常集群,然后通过copyTable工具扫快照生成HFile,然后bulk load 到异常集群,完成数据的修复 。
7.3.2、当用户写入大量数据后,发现选择的split keys不合适,想重新选择split keys见表,这时也可以通过 snapshort生成HFile再bulk load的方式生成新表 。
GRPC负载均衡负载均衡是高可用网络基础架构的关键组件,通常用于将工作负载分布到多个服务器来提高应用的性能和可靠性 。
关于GRPC的负载均衡,我们选择NGINX来进行反向代理 。NGINX在1.13.9版本已经开始支持GRPC代理 。有了对 GRPC 的支持,NGINX 就可以代理 GRPC TCP 连接,还可以终止、检查和跟踪 GRPC 的方法调用 。
在nginx的配置文件中配置GRPC代理即可 。
通过grpc 调用代理端口
观察NGINX的access.log,可以看到相应的代理记录
首先GRPC是建立在HTTP2.0的基础上进行数据传输,HTTP2.0的优势就不用多说了 。因此NGINX负载均衡同样也是建立在HTTP2.0的基础之上 。并且NGINX新增了GRPC模块 。用于负载GRPC请求 。
需要注意以下几个NGINX的参数配置:
nginx_http2模块
http2_max_requests:在一个tcp连接上默认通过的最大数据包数,默认1000个
http2_max_concurrent_streams:在一个tcp连接上默认最大并发流,默认128个
nginx_grpc模块
grpc_send_timeout:将请求传输到gRPC服务器的超时时间,如果超过这个时间,NGINX会断开连接 。
grpc_read_timeout:接收gRPC服务器数据的超时时间 , 如果超过这个时间,NGINX会断开连接 。
grpc_socket_keepalive:NGINX与gRPC服务器长连接配置设置 。
gohttp只获取header断开考虑基于HTTP的RPC , 或者HTTP服务器主动通知客户端的机制,就是HTTP Long-Polling,意思就是客户端发起一个长连接,服务器阻塞忍住不响应直到:
超时,比如5秒后 , 我们给客户端响应一个keepalive,意思是现在还没有啥事 , 请继续polling 。
拿到结果,这个可能是任何时候,比如300毫秒、1100毫秒、2300毫秒拿到一个事件,响应给客户端,实现了有事件异步通知 。
这样客户端和服务器之间RPC的效率就非常高,只有在有事件时才会通知 。但是,实际上还有一种情况需要处理:
当客户端断开连接,比如客户端设置了3秒钟TCP请求超时,或者因为客户端Crash时OS回收了FD等等,这个时候服务器应该要终止polling事务,停止获取事件 。因为如果这个时候获取了事件,那么如何处理这个事件?只能丢弃 , 如果客户端再次发起请求,就拿不到这个事件了 。
问题就来了 , 如何在HTTP Handler中探测客户端断开?例如:
建行到家rpc连接超时系统没有及时升级 。
建行到家rpc显示请求超时,主要原因就是系统没有及时升级 , 部分组件没有响应,必须更新 。
rpc(RemoteProcedureCall)是远程过程调用,它是一种通过网络从远程计算机程序上请求服务 , 而不需要了解底层网络技术的协议 。
【go语言rpc超时时间 go语言rpc框架】关于go语言rpc超时时间和go语言rpc框架的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息 , 记得收藏关注本站 。

    推荐阅读