Macos上跑Docker Desktop遇到registry-1.docker.io网络不通问题填坑笔记

问题: docker、openai、claudeai等国外的站点对国内IP有限制,所以在使用vpn等要注意一些点 原因: 当使用docker指令启动项目,报错 1 Error response from daemon: Get "https://registry-1.docker.io/v2/": EOF(base) wx@wxs-MacBook-Pro docker 是因为:registry-1.docker.io被墙 解决方法: 1)切记VPN要设置“全局代理” 在使用docker desktop、或者直接调用claude/chatgpt的open api时候,也需要设置“全局代理” 例如我用的VPN是XXXXXVpn,是设置“安全模式” 可以在一个网站查看是否生效:https://whatismyipaddress.com 如图,我的已经生效。如果一直不生效,请在无痕模式下打开浏览器 2)添加国内代理站点: 请将下面的代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 { "builder": { "gc": { "defaultKeepStorage": "20GB", "enabled": true } }, "debug": true, "dns": [ "8.8.8.8", "114.114.114.114" ], "experimental": true, "proxies": { "http-proxy": "http://127.0.0.1:7890", "https-proxy": "http://127.0.0.1:7890", "no-proxy": "localhost,127.0.0.0/8" }, "registry-mirrors": [ "https://hub.rat.dev" ] } 拷贝到 Docker Desktop的 设置按钮–> Docker Engine 文本框 对应文件的磁盘存储路径为:~/.docker/daemon.json

2024/11/8
articleCard.readMore

RocketMQ的死信队列你了解多少?怎么实现的?

​ 在使用MQ的过程中,常常有这种情形:生产者向一个topic发送消息后,如果消费者一直消费失败。要确保消息不被丢弃,直到消费者服务正常消费这个条消息为止。这个在RocketMQ 中的实现正是死信队列。 ​

2019/12/8
articleCard.readMore

Dubbo服务间的连接是怎么控制处理的?

​ 最近被问到一个问题: 有依赖关系的两个dubbo服务。通过TCP进行连接时候,他们之间的连接是怎么控制的?怎么达到一个合理的数量? ​ 我们从一个例子开始吧:一个订单服务 OrderService,IP为192.168.0.110 连接了商品服务 ProductService, ip 为192.168.0.111 ,其中订单服务中的方法比较多,并且很多请求也刚好路由到192.168.0.111的 ProductService 服务。那问题就来了:110机器作为客户端是怎么控制连接数的?会不会无限量地和111机器进行TCP连接? ​ 我们先看一下Dubbo的官方文档对“连接控制”的说明文档 : http://dubbo.apache.org/zh-cn/docs/user/demos/config-connections.html 。 ​ 在xml配置方式中xml accepts="10" 和 connections="10" 分别在服务端和客户端进行了相应的连接控制。下面我们看一下源码,追一下连接控制的原理。 ​ 我们看一下DubboProtocol.java的创建客户端tcp连接的方法,int connectNum正是每个客户端对服务端的tcp连接数,默认是1,当然可以修改成更大。默认是1,这样一个客户端的调用service数最多也不会超过1000吧。这样就不会出现单机创建TCP连接数过多的问题。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 /** * Bulk build client * * @param url * @param connectNum * @return */ private List<ReferenceCountExchangeClient> buildReferenceCountExchangeClientList(URL url, int connectNum) { List<ReferenceCountExchangeClient> clients = new ArrayList<>(); for (int i = 0; i < connectNum; i++) { clients.add(buildReferenceCountExchangeClient(url)); } return clients; } 客户端和服务端是一对一的,建立长连接,那么如果客户端并发访问,他们是怎么和服务端交互的呢? 经过看代码: 下面咱们试图从代码中找到痕迹。一路追踪,我们来到这个类:com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeChannel.java,先来看看其中的request方法,大概在第101行左右: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public ResponseFuture request(Object request, int timeout) throws RemotingException { if (closed) { throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!"); } // create request. Request req = new Request(); req.setVersion("2.0.0"); req.setTwoWay(true); req.setData(request); //这个future就是前面我们提到的:客户端并发请求线程阻塞的对象 DefaultFuture future = new DefaultFuture(channel, req, timeout); try{ channel.send(req); //非阻塞调用 }catch (RemotingException e) { future.cancel(); throw e; } return future; } 注意这个方法返回的ResponseFuture对象,当前处理客户端请求的线程在经过一系列调用后,会拿到ResponseFuture对象,最终该线程会阻塞在这个对象的下面这个方法调用上,如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public Object get(int timeout) throws RemotingException { if (timeout <= 0) { timeout = Constants.DEFAULT_TIMEOUT; } if (! isDone()) { long start = System.currentTimeMillis(); lock.lock(); try { while (! isDone()) { //无限连 done.await(timeout, TimeUnit.MILLISECONDS); if (isDone() || System.currentTimeMillis() - start > timeout) { break; } } } catch (InterruptedException e) { throw new RuntimeException(e); } finally { lock.unlock(); } if (! isDone()) { throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false)); } } return returnFromResponse(); } 上面我已经看到请求线程已经阻塞,那么又是如何被唤醒的呢?再看一下com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.java,其实所有实现了ChannelHandler接口的类都被设计为装饰器模式,所以你可以看到类似这样的代码: 1 2 3 4 5 6 protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) { return new MultiMessageHandler( new HeartbeatHandler( ExtensionLoader.getExtensionLoader(Dispather.class).getAdaptiveExtension().dispath(handler, url) )); } 现在来仔细看一下HeaderExchangeHandler类的定义,先看一下它定义的received方法,下面是代码片段: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public void received(Channel channel, Object message) throws RemotingException { channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis()); ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel); try { if (message instanceof Request) { ..... } else if (message instanceof Response) { //这里就是作为消费者的dubbo客户端在接收到响应后,触发通知对应等待线程的起点 handleResponse(channel, (Response) message); } else if (message instanceof String) { ..... } else { handler.received(exchangeChannel, message); } } finally { HeaderExchangeChannel.removeChannelIfDisconnected(channel); } } 我们主要看中间的那个条件分支,它是用来处理响应消息的,也就是说当dubbo客户端接收到来自服务端的响应后会执行到这个分支,它简单的调用了handleResponse方法,我们追过去看看: 1 2 3 4 5 static void handleResponse(Channel channel, Response response) throws RemotingException { if (response != null && !response.isHeartbeat()) { //排除心跳类型的响应 DefaultFuture.received(channel, response); } } 熟悉的身影:DefaultFuture,它是实现了我们上面说的ResponseFuture接口类型,实际上细心的童鞋应该可以看到,上面request方法中其实实例化的就是这个DefaultFutrue对象: 1 DefaultFuture future = new DefaultFuture(channel, req, timeout); 那么我们可以继续来看一下DefaultFuture.received方法的实现细节: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static void received(Channel channel, Response response) { try { DefaultFuture future = FUTURES.remove(response.getId()); if (future != null) { future.doReceived(response); } else { logger.warn("The timeout response finally returned at " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())) + ", response " + response + (channel == null ? "" : ", channel: " + channel.getLocalAddress() + " -> " + channel.getRemoteAddress())); } } finally { CHANNELS.remove(response.getId()); } } 留一下我们之前提到的id的作用,这里可以看到它已经开始发挥作用了。通过id,DefaultFuture.FUTURES可以拿到具体的那个DefaultFuture对象,它就是上面我们提到的,阻塞请求线程的那个对象。好,找到目标后,调用它的doReceived方法,这就是标准的java多线程编程知识了: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 private void doReceived(Response res) { lock.lock(); try { response = res; if (done != null) { done.signal(); } } finally { lock.unlock(); } if (callback != null) { invokeCallback(callback); } } 这样我们就可以证实上图中左边的绿色箭头所标注的两点。 参考链接:https://blog.csdn.net/joeyon1985/article/details/51046548

2019/12/3
articleCard.readMore

Seata 基本原理和源码分析

2019/10/31
articleCard.readMore

分布式事务一致性算法Paxos、raft、zab等的比较

2019/10/31
articleCard.readMore

谈谈各种流程引擎、规则引擎

开源: jbpm activiti drools QLexpress 流程引擎 规则引擎 脚本引擎 区别 分别用在什么业务上? 工作流 商业产品: 普元:bps eos

2019/10/31
articleCard.readMore

从netty组件的传输讲讲io

2019/10/31
articleCard.readMore

JVM内存结构的历史 (从Jdk1.6、1.7、8)

从JDK1.6到1.8, 运行时内存分配简图分别如下: 在JDK1.7中的HotSpot中,已经把原本放在方法区的字符串常量池移出。   从JDK7开始永久代的移除工作,贮存在永久代的一部分数据已经转移到了Java Heap或者是Native Heap。但永久代仍然存在于JDK7,并没有完全的移除:符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap。 随着JDK8的到来: JVM不再有PermGen。但类的元数据信息(metadata)还在,只不过不再是存储在连续的堆空间上,而是移动到叫做“Metaspace”的本地内存(Native memory)中。 一、java7到java8的第一部分变化:元空间 下面来一张图看一下java7到8的内存模型吧(这个是在网上找的图,如有侵权问题请联系我删除。) 二、java7到java8的第二部分变化:运行时常量池 运行时常量池(Runtime Constant Pool)的所处区域一直在不断的变化,在java6时它是方法区的一部分;1.7又把他放到了堆内存中;1.8之后出现了元空间,它又回到了方法区。 Metaspace 结构是怎么样的? 参考:https://blog.csdn.net/weixin_42711325/article/details/86533192

2019/10/31
articleCard.readMore

怎么定位线上的内存溢出?

2019/10/31
articleCard.readMore

MQ组件盘点,哪些你用在了生产中?

市面上的MQ也好几种了,ActiveMq、RabbitMq、rocketMq、kafka、Pulsar。最近国内又陆陆续续开源了几个MQ,如:去哪儿网开源的qmq、腾讯开源的TubeMq、拍拍贷开源的pmq。 现在想需要对比区别一下这些消息队列的不同,分析其优缺点。 一、基本比较 ActiveMQRabbitMQRocketMQKafkaPulsar 代码地址apache/activemqapache/rabbitmq-serverapache/rocketmqapache/kafkaapache/pulsar PRODUCER-COMSUMER支持支持支持支持支持 PUBLISH-SUBSCRIBE支持支持支持支持支持 REQUEST-REPLY支持支持 API完奋性高高高高 多语言支持支持,IAVA优先语言无关支持支持,java优先 单机吞吐量万级万级万级十万级单个分区高达 1.8 M 消息/秒 消息延迟微秒级毫秒级毫秒级99% 的生产延迟小于5ms。 可用性高(主从)高(主从)非常高(分布式)非常高(分布式)高 消息丢失低低理论上不会丢失理论上不会丢失 消息重复可控制理论上会有重复 文挡完备性高高高高高 提供快速入门有有有有有 首次部署难度低中高 社区活跃度高高高高高 商业支持无无阿里云无 成熟度成熟成熟成熟成熟日志领域 支持协议OpenWire、STOMP、REST、 XMPP、AMQPAMQP白己定义的一套,社区提供JMS,不成熟) 持久化内存、文件、数据库内存、文件磁盘文件PageCache ->磁盘Apache BookKeeper 事务支持支持支持 负载均衡支持支持支持 管理界面一般好有web console实现 部署方式独立、嵌入独立独立 特点功能齐全,被大 望开源项目使用由于Erlang语言的并发能力,性能很好各个环节分布式扩展设计,主从HA;支持上万个队列;多 种消费模式;性能很好 评价:优点成熟的产品,已经在很多公司得到应用(非大规横场景);有较多的文档;备种协议支持较好;有多重语富的成熟的客户端;由于erlang语富的特 性,mq性能较好;管埋界面 较丰富,在互联网公司也有 较大规棋的应用;支持amqp协议,有多种语言且支持 amqp的客户端可用;模型简单,接口易用(JMS接口在很多场合并不太实用);在阿里大规棋应用;目前支付宝中的余额宝等新兴产品均使用rocketmq;集群规棋大槪在50台左右,单日处理消息上百亿;性能非常好,可以大量消息堆积在broker中;支持多种消费:包括集群消费、广播消费等;社区活跃,版本更新很快。地域复制、多租户、扩展性、读写隔离等等;对 Kubernetes 的友好支持。 评价:缺点根据其他用户反馈,会出现莫名其妙的问题,且会丢消息。目前社区不活跃;不适合用于上千个队列的应用场景。erlang语言难度较大。集群不支持动态扩展。多语言客户端支持需加强部署相对复杂;新来者,文档较少 二、各自优缺点 1、Kafka 大数据行业标配组件 2、RocketMq ​ 有事务性消息、私信队列等支持,适合交易场景 3、Pulsar ​ 新贵,地域复制、多租户、扩展性比较好 4、RabbitMq ​ erlang编写,性能较好。有不少互联网公司用。不过因为erlang,社区开发者较少 5、ActiveMq 项目较老,不够活跃,会丢消息,不适合在互联网项目使用 三、一些问题 1、Kafka的数据丢失问题 一开始就是存储在PageCache上的,定期flush到磁盘上的,也就是说,不是每个消息都被存储在磁盘了,如果出现断电或者机器故障等,PageCache上的数据就丢失了。 这个是总结出的到目前为止没有发生丢失数据的情况 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 //producer用于压缩数据的压缩类型。默认是无压缩。正确的选项值是none、gzip、snappy。压缩最好用于批量处理,批量处理消息越多,压缩性能越好 props.put("compression.type", "gzip"); //增加延迟 props.put("linger.ms", "50"); //这意味着leader需要等待所有备份都成功写入日志,这种策略会保证只要有一个备份存活就不会丢失数据。这是最强的保证。, props.put("acks", "all"); //无限重试,直到你意识到出现了问题,设置大于0的值将使客户端重新发送任何数据,一旦这些数据发送失败。注意,这些重试与客户端接收到发送错误时的重试没有什么不同。允许重试将潜在的改变数据的顺序,如果这两个消息记录都是发送到同一个partition,则第一个消息失败第二个发送成功,则第二条消息会比第一条消息出现要早。 props.put("retries ", MAX_VALUE); props.put("reconnect.backoff.ms ", 20000); props.put("retry.backoff.ms", 20000); //关闭unclean leader选举,即不允许非ISR中的副本被选举为leader,以避免数据丢失 props.put("unclean.leader.election.enable", false); //关闭自动提交offset props.put("enable.auto.commit", false); 限制客户端在单个连接上能够发送的未响应请求的个数。设置此值是1表示kafka broker在响应请求之前client不能再向同一个broker发送请求。注意:设置此参数是为了避免消息乱序 props.put("max.in.flight.requests.per.connection", 1); 2、Kafka重复消费原因 强行kill线程,导致消费后的数据,offset没有提交,partition就断开连接。比如,通常会遇到消费的数据,处理很耗时,导致超过了Kafka的session timeout时间(0.10.x版本默认是30秒),那么就会re-blance重平衡,此时有一定几率offset没提交,会导致重平衡后重复消费。 如果在close之前调用了consumer.unsubscribe()则有可能部分offset没提交,下次重启会重复消费 kafka数据重复 kafka设计的时候是设计了(at-least once)至少一次的逻辑,这样就决定了数据可能是重复的,kafka采用基于时间的SLA(服务水平保证),消息保存一定时间(通常为7天)后会被删除

2019/10/31
articleCard.readMore

一些有用的github配置

Github是Git的一个商业化平台,git对应的一些功能在Github上都有体现。 在github的使用过程中,一些有用的github配置,明显改进了流程,方便了开发,随手记录了下来。 1、pr完毕联动直接关闭issue : 功能: 当committer合并别人提交的pull request的时候,同时联动关闭问题issue 配置: https://help.github.com/cn/articles/closing-issues-using-keywords 2、设置成sqash 合并提交点: 功能: 当committer合并别人提交的选择将提交点进行合并,类似于指令git squash 配置: https://help.github.com/cn/articles/about-merge-methods-on-github 3、更新其他人的分支代码: 功能:在合作开发中,一些分支的提交者好久没有活动了,远程分支已经修改了很多东西。 committer或者pr的提交可以在github界面手动同步主分支的最新代码. 类似于指令 1 2 git remote update -p git pull origin master 配置方法如下: 效果: 配置: https://help.github.com/cn/articles/enabling-required-status-checks 4、添加测试覆盖率travis-ci 和codecov 功能: 用junit、travis-ci 和codecov等采集项目的测试覆盖率,从而进行持续集成 配置: Travis CI + Codecov + Junit5 + jacoco + Maven + java8 above Java Example https://github.com/lovepoem/codecov-travis-maven-junit5-example Travis CI + Codecov + Junit4 + cobertura + java1.7 + Maven Java Example https://github.com/lovepoem/codecov-travis-maven-junit4-example 5、将issue指派给项目的contributor 最早时候github不支持将一个issue指派给对项目没有写权限的用户。在2019年下半年开始,github增加了“Triage”角色:没有项目的直接写权限,却能review pr的人。首先issue可以指派给“Triage”角色;一般用户只要在issue上加评论,就可以将issue指派给她。

2019/7/23
articleCard.readMore

我看《subscribed》

前一段时间在朋友圈看到国内开源界大佬姜宁和冯嘉在推荐一本《subscribed》, ​ 《订阅》,我是最近在朋友圈看到这个本书的。两个开源股界的大佬姜宁和冯嘉同时推荐这本书的。好奇后读起来。发现完全是一本商科的书呀,在推一个观点:各行各业的未来都会用“订阅”的模式进行发展。 ​ 订阅的含义是啥??用图画一下订阅模式。一般情况:订报刊。搞技术的:消息队列,异步。为什么异步的模式是未来商业模式的方向和趋势?重点 ​ 将书中各个行业的异步商业模型简要讲解,必要时候画出流程图。 ​ 自己切身感受和思考。在软件架构方面,事情本来的样子是什么?异步编程,消息驱动。开源社区的运营和商业化方面:吸引同好,

2019/7/22
articleCard.readMore

为什么Redis读写快?

问题一: Redis是单线程还是多线程?为什么redis会被认为读写都快呢? 单线程的, Redis6 之后多线程IO 1.redis是基于内存的,内存的读写速度非常快; 2.redis是单线程的,省去了很多上下文切换线程的时间; 3.redis使用多路复用技术,可以处理并发的连接。非阻塞IO 内部实现采用epoll,采用了epoll+自己实现的简单的事件框架。epoll中的读、写、关闭、连接都转化成了事件,然后利用epoll的多路复用特性,绝不在io上浪费一点时间。 问题二:Redis都有什么数据结构,内部存储数据结构是啥? ​ String——字符串 简单动态字符串(SDS[Simple Dynamic String]) ​ Hash——字典 ​ List——列表 ​ Set——集合 ​ Sorted Set——有序集合 skiplist数据结构  skiplist作为zset的存储结构,整体存储结构如下图,核心点主要是包括一个dict对象和一个skiplist对象。dict保存key/value,key为元素,value为分值;skiplist保存的有序的元素列表,每个元素包括元素和分值。两种数据结构下的元素指向相同的位置。 问题三 Redis的持久化策略有哪些? Redis两种持久化方式(RDB&AOF) RDB每次进行快照方式会重新记录整个数据集的所有信息。RDB在恢复数据时更快,可以最大化redis性能,子进程对父进程无任何性能影响。 AOF有序的记录了redis的命令操作。意外情况下数据丢失甚少。AOF同步也是一种把记录写到硬盘上的行为,在上述两个步骤之外,Redis额外加一步命令,Redis先把记录追加到自己维护的一个aof_buf中。所以AOF持久化分为三步:1、命令追加 2、文件写入 3.文件同步

2019/2/13
articleCard.readMore

Dubbo代码分析—超时和重试设计

dubbo提供在provider和consumer端,都提供了超时(timeout)和重试(retries)的参数配置。 一、Dubbo协议中的超时机制 ​ 超时机制是dubbo中的一个很重要的机制。在“快速失败”设计中,能将客户端断掉,防止服务端资源耗尽而被压挂。 ​ 我们先看一下Dubbo协议中的超时机制是怎么实现的。 ​ 超时的实现原理是什么? 我们先回顾一下dubbo的大致流程图。 在生产过程中都是客户端向服务端发送请求,在流程4。 ​ 我们可以看到:4的流程是: 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。 ​ 超时是针对消费端还是服务端? 如果是争对消费端,那么当消费端发起一次请求后,如果在规定时间内未得到服务端的响应则直接返回超时异常,但服务端的代码依然在执行。 如果是争取服务端,那么当消费端发起一次请求后,一直等待服务端的响应,服务端在方法执行到指定时间后如果未执行完,此时返回一个超时异常给到消费端。 dubbo的超时是争对客户端的,由于是一种NIO模式,消费端发起请求后得到一个ResponseFuture,然后消费端一直轮询这个ResponseFuture直至超时或者收到服务端的返回结果。虽然超时了,但仅仅是消费端不再等待服务端的反馈并不代表此时服务端也停止了执行。 ​ 超时的实现原理是什么? 之前有简单提到过, dubbo默认采用了netty做为网络组件,它属于一种NIO的模式。消费端发起远程请求后,线程不会阻塞等待服务端的返回,而是马上得到一个ResponseFuture,消费端通过不断的轮询机制判断结果是否有返回。因为是通过轮询,轮询有个需要特别注要的就是避免死循环,所以为了解决这个问题就引入了超时机制,只在一定时间范围内做轮询,如果超时时间就返回超时异常。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 //ResponseFuture接口定义 public interface ResponseFuture { /** * get result. * * @return result. */ Object get() throws RemotingException; /** * get result with the specified timeout. * * @param timeoutInMillis timeout. * @return result. */ Object get(int timeoutInMillis) throws RemotingException; /** * set callback. * * @param callback */ void setCallback(ResponseCallback callback); /** * check is done. * * @return done or not. */ boolean isDone(); } //ReponseFuture的实现类:DefaultFuture //只看它的get方法,可以清楚看到轮询的机制。 public Object get(int timeout) throws RemotingException { if (timeout <= 0) { timeout = Constants.DEFAULT_TIMEOUT; } if (! isDone()) { long start = System.currentTimeMillis(); lock.lock(); try { while (! isDone()) { done.await(timeout, TimeUnit.MILLISECONDS); if (isDone() || System.currentTimeMillis() - start > timeout) { break; } } } catch (InterruptedException e) { throw new RuntimeException(e); } finally { lock.unlock(); } if (! isDone()) { throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false)); } } return returnFromResponse(); } 超时解 超时的实现原理是什么? 之前有简单提到过, dubbo默认采用了netty做为网络组件,它属于一种NIO的模式。消费端发起远程请求后,线程不会阻塞等待服务端的返回,而是马上得到一个ResponseFuture,消费端通过不断的轮询机制判断结果是否有返回。因为是通过轮询,轮询有个需要特别注要的就是避免死循环,所以为了解决这个问题就引入了超时机制,只在一定时间范围内做轮询,如果超时时间就返回超时异常。 二、Dubbo协议中的重试机制 在客户端 int DEFAULT_RETRIES = 2; 看到重试次数是2 ,就是说出了本身的一次请求,再失败后,又会再请求一次。 重试策略在集群的失败重试 FailoverClusterInvoker 的策略中: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException { List<Invoker<T>> copyInvokers = invokers; checkInvokers(copyInvokers, invocation); String methodName = RpcUtils.getMethodName(invocation); int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1; if (len <= 0) { len = 1; } // retry loop. RpcException le = null; // last exception. List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size()); // invoked invokers. Set<String> providers = new HashSet<String>(len); for (int i = 0; i < len; i++) { //Reselect before retry to avoid a change of candidate `invokers`. //NOTE: if `invokers` changed, then `invoked` also lose accuracy. if (i > 0) { checkWhetherDestroyed(); copyInvokers = list(invocation); // check again checkInvokers(copyInvokers, invocation); } Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked); invoked.add(invoker); RpcContext.getContext().setInvokers((List) invoked); try { Result result = invoker.invoke(invocation); ...... 可以看到使用的是for循环做的失败重试的。当有异常发生时候,就重试一次访问,直到达到最大重试次数为止。 参考:https://www.cnblogs.com/xuwc/p/8974709.html ​

2019/2/11
articleCard.readMore

【译】apache 基金会角色列表

角色 精英管理通常在每个Apache项目社区中都有不同的角色: User(用户) ​ 用户是指使用 apache 软件的人。他们通过以错误报告和功能建议的形式向开发人员提供反馈,为Apache项目做出贡献。用户通过在邮件列表和用户支持论坛上帮助其他用户来参与Apache社区的建设。 Contributor (开发者/贡献者) 开发者是用代码或文档形式个 apache 项目做贡献的用户。他们采取额外步骤参与项目,积极参与开发人员邮件列表,参与讨论,提供补丁,文档,建议和批评。开发人员也被称为贡献者。 Committer (提交者) 提交者是签订了贡献者许可协议(CLA)文件,并且被授予了 apache 代码库的写入权限的开发者。他们有一个apache.org邮件地址。他们不需要依赖其他人来打补丁,他们实际上正在为项目做出短期决策。PMC可以(甚至是默许)同意并批准它成为永久性的,或者他们可以拒绝它。但是请记住,是PMC做出的决定,而不是单个提交者。 PMC Member (PMC会员) PMC会员由于项目的发展和承诺的证明而当选的开发商或委员会成员。他们拥有对代码存储库的写访问权、apache.org邮件地址、对与社区相关的决策进行投票的权利以及向活动用户建议提交的权利。作为一个整体,项目管理咨询公司是控制项目的实体,没有其他人。特别是,项目管理委员会必须对其项目软件产品的任何正式发布进行投票。 PMC CHAIR (PMC主席) 项目管理委员会(PMC)的主席由董事会从PMC成员中任命。PMC作为一个整体是控制和领导项目的实体。主席是理事会与项目之间的接口。 PMC主席有特定的职责。 ASF MEMBER (ASF会员) ASF成员是由现任成员提名并因基金会的发展和进步而当选的人。会员关心ASF本身。这通常通过与项目相关和跨项目活动的根源来证明。法律上,会员是基金会的“股东”之一。他们有权选举董事会,作为董事会选举的候选人,并提名一名委员为成员。他们也有权提出一个新的孵化项目(稍后我们将看到这意味着什么)。成员通过邮件列表和年度会议协调其活动。我们有一个完整的Apache成员列表。 原文地址:http://apache.org/foundation/how-it-works.html#roles

2019/2/4
articleCard.readMore

Dubbo代码分析---服务端线程池

一、Dubbo服务端线程池耗尽问题 在使用Dubbo的过程中,在服务端压力大时候我们常常会遇到说线程池耗尽的这样一个错误日志: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 17:54:34,026 WARN [New I/O server worker #1-4] - [DUBBO] Thread pool is EXHAUSTED! Thread Name: DubboServerHandler-10.8.64.57:20880, Pool Size: 300 (active: 300, core: 300, max: 300, largest: 300), Task: 5821 (completed: 5621), Executor status:(isShutdown:false, isTerminated:false, isTerminating:false), in dubbo://x.x.x.x:20880!, dubbo version: 2.6.5, current host: x.x.x.x com.alibaba.dubbo.remoting.ExecutionException: class com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler error when process caught event . at com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler.caught(AllChannelHandler.java:67) at com.alibaba.dubbo.remoting.transport.AbstractChannelHandlerDelegate.caught(AbstractChannelHandlerDelegate.java:44) at com.alibaba.dubbo.remoting.transport.AbstractChannelHandlerDelegate.caught(AbstractChannelHandlerDelegate.java:44) at com.alibaba.dubbo.remoting.transport.AbstractPeer.caught(AbstractPeer.java:127) at com.alibaba.dubbo.remoting.transport.netty.NettyHandler.exceptionCaught(NettyHandler.java:112) at com.alibaba.dubbo.remoting.transport.netty.NettyCodecAdapter$InternalDecoder.exceptionCaught(NettyCodecAdapter.java:165) at org.jboss.netty.channel.Channels.fireExceptionCaught(Channels.java:432) at org.jboss.netty.channel.AbstractChannelSink.exceptionCaught(AbstractChannelSink.java:52) at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:302) at com.alibaba.dubbo.remoting.transport.netty.NettyCodecAdapter$InternalDecoder.messageReceived(NettyCodecAdapter.java:148) at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:274) at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:261) at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:350) at org.jboss.netty.channel.socket.nio.NioWorker.processSelectedKeys(NioWorker.java:281) at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:201) at org.jboss.netty.util.internal.IoWorkerRunnable.run(IoWorkerRunnable.java:46) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:619) Caused by: java.util.concurrent.RejectedExecutionException: Thread pool is EXHAUSTED! Thread Name: DubboServerHandler-10.8.64.57:20880, Pool Size: 300 (active: 300, core: 300, max: 300, largest: 300), Task: 5821 我们从错误堆栈可以看到,是服务端线程池溢出了。 二、源码分析 看一下服务端线程池代码是怎么写的,起到什么作用 画图 dubbo客户端调用服务端线程池的示意图 三、线程池知识 解释一下线程池的定义是用来干嘛的。 一些处理的办法: 适当加大服务端线程池,找到合理的配置。这个关联一线程池的配合准则,链接到老的文章。 四、分析原因,处理建议 为什么线程池升高呢? 主要从流量、cpu数据率、负载、jvm日志几方面分析 1)流量过大?(加缓存、对于上游上游服务) 2) 扩机器:cpu核数的增加 3) 宿主机此时负载高? 4)自己代码优化 5) 设置客户端快速失败 程序慢?io过多?(并行io)

2019/1/1
articleCard.readMore

rpc跟踪日志常用shell统计脚本

一、应用日志分析 rpc跟踪日志作为时间序列数据,一般字段有:时间、traceId、接口名、执行时间ms、执行结果等。笔者将自己常用的shell统计脚本记录下来,也希望读大家有帮助。 下面的日志信息trace.log,字段分别是:时间| traceId|接口名|执行时间ms|执行结果 1 2 3 4 5 6 7 8 9 10 11 2018-10-01 14:00:00|1|getById|1|success| 2018-10-01 03:01:00|2|getById|100|fail| 2018-10-02 02:00:00|3|getById|1000|success| 2018-10-01 02:00:00|4|updateById|1000|success| 2018-10-01 02:00:00|5|getById|1000|success| 2018-10-01 02:00:00|6|updateById|200|success| 2018-10-02 14:56:00|7|updateById|20|success| 2018-10-01 02:00:00|8|updateById|60|fail| 2018-10-01 03:00:00|9|updateById|200|success| 2018-10-01 02:02:00|10|updateById|20|success| 2018-10-01 03:02:00|11|insert|20|success| 请实现如下需求: 1、执行结果大于200ms的记录 1 awk -F "|" '{if($4>=200){print $1" "$2" "$3" "$4 }}' trace.log 执行结果 1 2 3 4 5 2018-10-02 02:00:00 3 getById 1000 2018-10-01 02:00:00 4 updateById 1000 2018-10-01 02:00:00 4 getById 1000 2018-10-01 02:00:00 5 updateById 200 2018-10-01 03:00:00 8 updateById 200 2、2018-10-01 日接口数量排行前3 1 awk '/2018-10-01/' trace.log |awk -F "|" '{print $3}' |sort|uniq -c|sort -rn| head -3 执行结果 1 2 3 5 updateById 3 getById 1 insert 3、对于各个接口的执行时间ms按照(0-50,50-100,100-300,300以上)范围进行数量统计 1 awk -F "|" '{totalCnt[$3]++;if($4<=50){ms50[$3]++};if($4>50 && $4<=100){ms100[$3]++};if($4>100 && $4<=300){ms300[$3]++};if($4>300){ms300b[$3]++}}END{for(i in totalCnt)print i,int(ms50[i]),int(ms100[i]),int(ms300[i]),int(ms300b[i])}' trace.log 执行结果 1 2 3 getById 1 1 0 2 updateById 2 1 2 1 insert 1 0 0 0 4、查询trace.log文件各个接口的失败率 1 awk -F "|" '{totalCnt[$3]++;if($5=="fail"){failCnt[$3]++}}END{for(i in totalCnt)print i,(failCnt[i]/totalCnt[i])*100"%"}' trace.log 执行结果 1 2 3 getById 25% updateById 16.6667% insert 0% 5、查询trace.log各个接口的平均耗时 1 awk -F "|" '{totalCnt[$3]++;{rtSum[$3]+=$4}}END{for(i in totalCnt)print i,(rtSum[i]/totalCnt[i])}' trace.log 执行结果 1 2 3 getById 525.25 updateById 250 insert 20 二 、简单数据处理 1、删除id.txt重复id 1 2 3 4 5 6 7 8 aaaaa 2 111 2 111 aaaaa aaaaa aaaaa 脚本 1 cat id.txt | sort | uniq 执行结果 1 2 3 111 2 aaaaa 2、递归查看某个目录磁盘占用大小 1 du -h --max-depth=2 /data 一些参考: http://techslides.com/grep-awk-and-sed-in-bash-on-osx http://www.grapenthin.org/teaching/geop501/lectures/lecture_10_unix2.pdf http://coewww.rutgers.edu/www1/linuxclass2005/lessons/lesson9/shell_script_tutorial.html http://blog.chinaunix.net/uid-26736384-id-5756343.html

2018/12/31
articleCard.readMore

并发 - 谈谈HashMap和ConcurrentHashMap

1、HashMap和ConcurrentHashMap的实现基本数据结构是啥? 2、ConcurrentHashMap怎么做到线程安全的? 3、java8的,ConcurrentHashMap实现有哪些变化?

2018/10/31
articleCard.readMore

JVM基本结构

一、什么是 Java GC (垃圾回收) ? ​ 我们知道,java语言不像C++,将内存的分配和回收给程序员来处理。java用统一的垃圾回收机制来管理java进程的内存,就是通常所说的垃圾回收:GC(Garbage Collection)。 先到维基百科上看一下Garbage Collection的概念: ​ In computer science, garbage collection (GC) is a form of automatic memory management. The garbage collector, or just collector, attempts to reclaim garbage, or memory occupied by objectst that are no longer in use by the program. Garbage collection was invented by John McCarthy around 1959 to simplify manual memory management in Lisp. ​ 这就面临一些问题:java进程到底是什么样的结构?java进程的内存哪些需要被回收,在什么条件下才回收,谁来回收?我们首先来看一些概念 二、什么是JVM ? 从维基百科上看jvm的定义: A Java virtual machine (JVM) is a virtual machine that enables a computer to run Java programs as well as programs written in other languages that are also compiled to Java bytecode. 三、Jvm运行时数据区 在Java语言中,采用的是共享内存模型来实现多线程之间的信息交换和数据同步的。 根据《Java虚拟机文档》 第七版的规定,Java虚拟机锁管理的内存包含下面几个运行时数据区域: 方法区、虚拟机栈、本地方法栈、队、程序计数器等。 程序计数器: 一块较小的内存空间,可以看做是当前线程所执行的字节码的行数指示器。 JAVA虚拟机栈: 虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。 局部变量表存放了编译器可知的: 基本数据类型(boolean、byte、char、short、int、float、long、double) 对象引用:reference类型,可能是一个指向对象起始地址的引用指针,也可能是一个指向对象起始地址的引用指针 returnAddress地址:指向了一条字节码指令的地址 本地方法栈: 为虚拟机使用到的Native方法服务,与虚拟机栈的作用类似。 JAVA堆: Java堆是被所有线程共享的一块内存区域,此内存区域的唯一目的就是存放内存实例,所有的对象实例以及数组都要在堆上分配,是垃圾收集管理的主要区域。 方法区: 和Java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编辑器编译后的代码等 运行时常量池 是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有常量池来存放编译期生成的各种字面量和符号引用。 直接内存 对外内存,不是jvm的存储,所以不受jvm的堆参数控制。NIO可以直接分配对外内存,可能会导致OutOfMemoryError,导致java进程僵死。 四、对象的访问定位 ​ 我们Java程序需要通过栈上的reference数据来操作堆上的具体对象。 有两种方式:使用句柄访问和通过指针访问。 使用句柄 Java堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,句柄中包含了对象实例与类型数据各自的具体地址信息。 通过指针 Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而reference中存储的直接就是对象地址。 参考: 《深入理解java虚拟机》周志明 图片来自网络

2018/10/8
articleCard.readMore

血的教训:线程池定义一定要全局化,共享使用

一、发现问题 ​ 最近我们上线了一个比较底层的业务服务,上线后研发也去盯着服务器看了日志,没有发现有啥问题,就将服务全量上线了。谁知道过了15分钟左右,上游业务方打电话过来了,说报调用我们的业务服务出现超时。 这时候研发赶紧登上相关的机器去看,发现日志量很少,机器很卡。再有人去登入这台机器的时候就登入不了。研发初步考虑是网络问题或者宿主机问题,于是找来运维。 运维登入进去机器通过 1 ps -ef | wc -l 查看运行的进程数,结果为60000多。通过脚本查看linux进程的进程限制: 1 cat /etc/security/limits.conf ,结果是 1 2 root soft nofile 65535 root hard nofile 65535 ​ 进程总数差不多进程总数的限制。考虑到半小时前更新了新服务,去服务的日志看了看,原来tomcat有jvm内存溢出的日志: 1 2 java 进程out of memeory Java HotSpot(TM) 64-Bit Server VM warning: Exception java.lang.OutOfMemoryError occurred dispatching signal SIGTERM to handler- the VM may need to be forcibly terminatedy 二、查找和解决问题 ​ 通过上面的一些现象,赶紧将刚才的发布的机器回滚了,避免更大的问题。观察了半个小时,上游再也没有发现我们的服务报错的问题了。于是我们终端review了刚刚上线的一个pr, ​ 发现线程池的代码竟然定义在方法体里面: 1 2 3 4 5 6 7 8 public class BizService{ ...... pulic processBiz(){ ExecutorService fixedThreadPool = Executors.newFixedThreadPool(8); ...... } ...... } 这样每次调用这个方法,就构建一个新的线程池,开启了新的8个线程。又查找了《Java线程与Linux内核线程的映射关系》[1] , 发现:JVM线程跟内核轻量级进程有一一对应的关系。线程数达到60000多,处级进程上限65535。 每开启一个进程。 ​ 对于应用进程发生:out of memory,是因为多线程的开销内存超过jvm的最大堆内存限制。 没开启一个进程,都有内存开销,内存超过linux内存的上限了,怪不得机器快僵死。 ​ 为了验证这个猜想,于是将有错误的代码进行压测,果然问题复现,出OutOfMemoryError和机器被拖挂。然后将代码修改为: 1 2 3 4 5 6 7 8 public class BizService{ ExecutorService fixedThreadPool = Executors.newFixedThreadPool(8); ...... pulic processBiz(){ ...... } ...... } 然后进行了压测,问题没有复现,问题解决。 三、事后review和处理方案: ​ 复盘会议上,qa和相关人一起提出了如下方案来避免类似问题: 1、完善监控指标 ​ 本次线程池超限,java进程也有报错,就是报OutOfMemoryError,可是报警系统竟然没有第一时间收到。查了监控系统,在前一段项目进程了重构,日志路径修改了,原来的采集配置竟然没有同时修改。导致造成损失却没有收到警报。 2、代码review一定要做,并且认真做 ​ 本次程序员的线程池代码为低级失误,review认真是可以看出来的。 3、有大的优化要进行压测 ​ 本次pr,有线程池的使用,所以要进行压测 4、添加平台监控 ​ 添加平台监控,当linux进程大于20000个时候就就对进程数进行报警 参考: [1] http://blog.sina.com.cn/s/blog_605f5b4f010198b5.html 修改记录: 2019-07-18 丰富了案例的描述

2018/10/5
articleCard.readMore