作者归档:hongjiang

socketRead0阻塞2个半小时?

前几个月我们遇到过某些http请求会在本机会阻塞2个半小时左右最后成功返回的情况。经过排查发现问题并不在对方服务器,而是这2个半小时基本都是在自己的网络环境阻塞,通过dns端日志发现请求到达dns服务器也是2个半小时后。

在geek talk群里有人指出,glibc的低版本会在查完域名后把拿到的ip反过来查域名,dns服务器都不支持反解就一层层的传给其他dns,可能会变慢。redhat在6u fix了这个问题。但跟我们的情况不符。

当时的堆栈显示阻塞在socket的读取上(我不确定这里的连接是复用上次HttpURLConnection创建过的连接,还是首次跟对方建立连接),我google到有些人也遇到过相似的问题,hang在同样的代码上,疑似是虚拟机(kvm/vmware)或jdk网络层面的bug (JDK 1.8.0_65), 因为无法稳定的重现,没有进一步验证,记录一下这个诡异的问题

at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:170)
at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
at java.io.BufferedInputStream.read(BufferedInputStream.java:335)
- locked <0x0000000c868c460> (a java.io.BufferedInputStream)
at sun.net.www.MeteredStream.read(MeteredStream.java:134)
- locked <0x0000000c868c420> (a sun.net.www.http.KeepAliveStream)
at java.io.FilterInputStream.read(FilterInputStream.java:133)
at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(HttpURLConnection.java:3336)
at java.io.FilterInputStream.read(FilterInputStream.java:133)

回顾2016

看着小家伙一点一点的变化,从7个月开始无意识的冒出一些词语,到慢慢懂得叫爸爸妈妈,以及说出更多的词语和你不知所谓的句子。从爬行到第一次走路再到满地的跑,成长的过程中总会带给你一些惊喜。

我的境况没有什么变化,除了徒增一丝中年人的焦虑。生活本身就有诸多问题要去面对,处在这个时代的洪流中,被冲击的无招架之力,或许稍一松手你可能就错过了搭乘末班车的机会。

WassUp Real Time Analytics

独立blog的一个麻烦之处是维护成本,你需要投入额外的金钱和精力,其实vps一年的费用不过几百块(经常有折扣),更多可能是系统维护对普通人(非IT工作者)来说代价略高。我在系统维护方面投入的精力极少,一方面是阿里云的vps确实稳定,另一方面是我折腾的也较少,一直使用的固定模板和写作方式。

独立系统难避免的一点是容易受到攻击,对于wordpress来说你需要尽可能保证它是最新的版本。另外有一个很好的插件: WassUp Real Time Analytics,能够帮你做一些访问统计,并在管理界面上很容易发现疑似的攻击。

如何持续的写博客?

如何能持久的写博客?这是个挺难回答的问题,这个blog建立4年了,不长也不短;不过把之前在第三方博客平台上的记录也算进来,我写blog的时间也有十多年了。

博客有两种功能,第一是记录(或收集)信息,第二是社交。多数人没法持续下去可能是社交的期望更多,而写博客在社交方面得到的反馈是比较延迟的,也没法保证你的好友会读到,跟微博或朋友圈比起来传递速度和即时反馈感太滞后。

于我而言,博客作为信息整理(知识管理)的功能更突出一些。记录的过程常常会对内容重构,尤其对技术类文章,过程中会重新梳理可能遗漏或理解错误的地方。这些记录下来的内容更多是给自己提供便利的。

写作时尽量不要去在意别人的意见,甚至需不需要读者的反馈,也是个人的事。有些新的平台,比如微信的公众号或知乎专栏一类的交互方式可能更有利于传递和交互,但如果你的才思文笔不足以支撑起看客们围观喝彩,就不必声张。大部分人并不具备长期输出优质内容的能力,这种互动可能迫使你过多的想要展示好的一面,而难以维持。

独立平台的好处就在于你不会受到它的氛围以及审查影响;另外,这些新的平台未经过长时间的验证,或许五年后它们都消失掉了,而你的独立博客则可以长存。

《Scala函数式编程》中文版勘误2

感谢 shuai.xie 提出的这段漏掉的内容,这里补充一下。

这里b的类型声明并不是必须的。因为我们已经告诉Scala返回类型为B => C,Scala会从上下文获知b的类型,方法的实现部分只需要写为 b => f(a,b)就可以了。如果Scala能够推断出函数字面量的类型,就可以省略掉它的类型声明。

相关阅读:《Scala函数式编程》中文版勘误

记录几个实践中的问题

1) nginx禁止对写操作timeout时retry

以前遇到的一个case,业务那边说一笔请求从nginx端发送给后端tomcat了2次(落在两个不同的tomcat节点上)。后来发现是nginx发给后端节点timeout,然后做了重试,发给了另一个节点。默认情况下nginx对后端error 和 timeout 都会做retry,可以明确的禁止在timeout的情况下禁止retry。当然如果集群读写分离的话,对于只读集群retry是无所谓的,但对于写确实存在问题。

2) kafka重启时因为数据日志文件名被人重命名过而导致启动失败

启动kafka broker的时候,会重新load之前的每个topic的数据,正常情况下会提示每个topic恢复完成。

INFO Recovering unflushed segment 588022 in log xxx-topic-0. (kafka.log.Log)
INFO Completed load of log xxx-topic-0 with log end offset 590676 (kafka.log.Log)

但当有些topic下的数据恢复失败的时候,会导致broker关闭,异常如下

ERROR There was an error in one of the threads during logs loading: java.lang.NumberFormatException: For input string: "test" (kafka.log.LogManager)
FATAL [Kafka Server 3], Fatal error during KafkaServer startup. Prepare to shutdown (kafka.server.KafkaServer)

java.lang.NumberFormatException: For input string: "test"
      at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
      at java.lang.Long.parseLong(Long.java:589)
      at java.lang.Long.parseLong(Long.java:631)
      at scala.collection.immutable.StringLike$class.toLong(StringLike.scala:251)
      at scala.collection.immutable.StringOps.toLong(StringOps.scala:30)
      at kafka.log.Log$$anonfun$loadSegments$4.apply(Log.scala:152)
      at kafka.log.Log$$anonfun$loadSegments$4.apply(Log.scala:141)
      at scala.collection.TraversableLike$WithFilter$$anonfun$foreach$1.apply(TraversableLike.scala:778)
      at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
      at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:186)
      at scala.collection.TraversableLike$WithFilter.foreach(TraversableLike.scala:777)
      at kafka.log.Log.loadSegments(Log.scala:141)
      at kafka.log.Log.<init>(Log.scala:67)
      at kafka.log.LogManager$$anonfun$loadLogs$2$$anonfun$3$$anonfun$apply$7$$anonfun$apply$1.apply$mcV$sp(LogManager.scala:142)
      at kafka.utils.Utils$$anon$1.run(Utils.scala:54)
      at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
      at java.util.concurrent.FutureTask.run(FutureTask.java:266)
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
      at java.lang.Thread.run(Thread.java:745)

这是因为某个目录下,存在一个 test.log 的文件

$ ls mytopic-0/
00000000000000000485.index  00000000000000000485.log  00000000000000000568.index  00000000000000000568.log  test.log

看上去这个 test.log 当时是把 00…log 给拷贝了一个,然后用编辑器去查看内容。而事后忘了清理掉,导致重启时把这个文件当成一个畸形文件了。因为kafka broker要求所有数据文件名称都是Long类型的。

3) 又一个actor阻塞的例子

在我自己的mac上测试的时候,一切正常,部署到dev环境就严重超时。jstack观察发现又是误用阻塞操作导致所有actor的线程都被阻塞所致,当时 EventProcessor 这个 Router 背后的实例数设置的是40,而这台dev环境的linux只有2核,根据当时akka的配置里的并发因子算出并发线程数是32,所以32个线程基本都被 eventProcessor 的40个actor全给占用了,因为它是不断发消息轮询的(我的mac是8核,运行时的线程数要大于40不会发生全部被阻塞的情况)。解决方式,一方面调大并发因子,把线城数提升上去,另一方面控制 eventProcessor 的实例数,不让它的阻塞操作影响到其他actor。(其实根上是没设计好,没有隔离阻塞操作,只不过这正好是个小应用,不需要过多考虑。)

有道词典的异常堆栈

碰巧抓住了有道词典的异常页面,从堆栈里能看出他们的一些技术栈,他们使用的是Resin-3.0.21,一个十多年前曾流行过现在不太主流的应用容器,但在网易、搜狐、人人等公司这个容器依然很常见。他们用http去访问cassandra?

java.lang.NoSuchFieldError: INSTANCE
 at org.apache.http.conn.ssl.SSLConnectionSocketFactory.<clinit>(SSLConnectionSocketFactory.java:144)
 at org.apache.http.client.fluent.Executor.<clinit>(Executor.java:78)
 at org.apache.http.client.fluent.Request.execute(Request.java:177)
 at toolbox.cassandra.client.CassandraClient.httpGet(CassandraClient.java:47)
 at toolbox.cassandra.client.CassandraClient.init(CassandraClient.java:129)
 at toolbox.cassandra.client.CassandraClient.getInstance(CassandraClient.java:75)
 at outfox.dict.front.data.ugc.AsynUgcClient.<clinit>(AsynUgcClient.java:88)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
 at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
 at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:148)
 at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:87)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1000)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:953)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:487)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
 at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:628)
 at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
 at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
 at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:651)
 at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:599)
 at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:665)
 at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:518)
 at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:459)
 at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
 at javax.servlet.GenericServlet.init(GenericServlet.java:69)
 at outfox.dict.front.web.DictServlet.init(DictServlet.java:57)
 at com.caucho.server.dispatch.ServletConfigImpl.createServletImpl(ServletConfigImpl.java:646)
 at com.caucho.server.dispatch.ServletConfigImpl.createServlet(ServletConfigImpl.java:587)
 at com.caucho.server.dispatch.ServletManager.init(ServletManager.java:154)
 at com.caucho.server.webapp.Application.start(Application.java:1654)
 at com.caucho.server.deploy.DeployController.startImpl(DeployController.java:621)
 at com.caucho.server.deploy.StartAutoRedeployAutoStrategy.startOnInit(StartAutoRedeployAutoStrategy.java:72)
 at com.caucho.server.deploy.DeployController.startOnInit(DeployController.java:509)
 at com.caucho.server.deploy.DeployContainer.start(DeployContainer.java:153)
 at com.caucho.server.webapp.ApplicationContainer.start(ApplicationContainer.java:670)
 at com.caucho.server.host.Host.start(Host.java:420)
 at com.caucho.server.deploy.DeployController.startImpl(DeployController.java:621)
 at com.caucho.server.deploy.StartAutoRedeployAutoStrategy.startOnInit(StartAutoRedeployAutoStrategy.java:72)
 at com.caucho.server.deploy.DeployController.startOnInit(DeployController.java:509)
 at com.caucho.server.deploy.DeployContainer.start(DeployContainer.java:153)
 at com.caucho.server.host.HostContainer.start(HostContainer.java:504)
 at com.caucho.server.resin.ServletServer.start(ServletServer.java:971)
 at com.caucho.server.deploy.DeployController.startImpl(DeployController.java:621)
 at com.caucho.server.deploy.AbstractDeployControllerStrategy.start(AbstractDeployControllerStrategy.java:56)
 at com.caucho.server.deploy.DeployController.start(DeployController.java:517)
 at com.caucho.server.resin.ResinServer.start(ResinServer.java:546)
 at com.caucho.server.resin.Resin.init(Resin.java)
 at com.caucho.server.resin.Resin.main(Resin.java:625)

OneApm的问题

1) 我们没有使用它的大数据版本,普通版本在压测情况下服务器端如果吞吐不够,agent端会缓存大量的数据导致full gc,这里它占据了1G的内存

2) 对HttpURLConnection拦截时可能发生空指针异常,导致业务线程终止

java.lang.NullPointerException
 at com.blueware.monitor.bridge.reflect.ClassReflection$2.run(ClassReflection.java:30) ~[?:?]
 at com.blueware.monitor.bridge.reflect.ClassReflection$2.run(ClassReflection.java:28) ~[?:?]
 at java.security.AccessController.doPrivileged(Native Method) ~[?:1.8.0_65]
 at com.blueware.monitor.bridge.reflect.ClassReflection.loadClass(ClassReflection.java:28) ~[?:?]
 at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441) ~[?:1.8.0_65]
 at sun.net.www.protocol.http.HttpURLConnection.getHeaderField(HttpURLConnection.java:2979) ~[?:1.8.0_65]
 at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:489) ~[?:1.8.0_65]
 at com...

tomcat8.5.8遇到的两个问题

压力测试场景,前端nginx反向代理到4个tomcat实例,在其中的一个实例上产生了大量的countDownConnection Incorrect connection count警告

 WARNING [http-nio-8080-exec-48] org.apache.tomcat.util.net.AbstractEndpoint.countDownConnection Incorrect connection count, multiple socket.close called on the same socket.

另外一个异常是4个tomcat实例上都看到的NPE异常:

Exception in thread "http-nio-8080-AsyncTimeout" java.lang.NullPointerException
  at org.apache.coyote.AbstractProcessor.doTimeoutAsync(AbstractProcessor.java:528)
  at org.apache.coyote.AbstractProcessor.timeoutAsync(AbstractProcessor.java:518)
  at org.apache.coyote.AbstractProtocol$AsyncTimeout.run(AbstractProtocol.java:1130)
  at java.lang.Thread.run(Thread.java:745)

使用了servlet3.0,connector配置如下:

 <Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
           connectionTimeout="20000" redirectPort="8443" maxParameterCount="2000" maxKeepAliveRequests="-1"
       maxThreads="200" maxPostSize="20971520" acceptCount="1024" useBodyEncodingForURI="true"
        URIEncoding="UTF-8"/>

已将bug提交到了bugzilla,在这里记录一下,后续跟踪。