Linux Namespace

Namespace是Linux虚拟网络的一个重要概念,其实不光是虚拟网络,像现在最火的容器技术也是以Namespace为基石(另外一个是cgroups),所以了解Namespace是很有必要的。 传统的Linux的许多资源是全局的,比如进程ID资源,而Namespace的 目的首先就是把这些资源做资源隔离。Linux可以在一个Host内创建许多namespace,于是那些原本是Linux全局的资源,就变成了namespace范围内的“全局”资源,而且不同namespace的资源互相不可见,彼此透明。 Linux具体将哪些全局资源做了隔离呢?看Linux相应的代码最直观: //nsproxy.h struct nsproxy{ atomic_t count; struct uts_namespace *uts_ns; struct ipc_namespace *ipc_ns; struct mnt_namespace *mnt_ns; struct pid_namespace *pid_ns; struct user_namespace *user_ns;…

慢思考者

今天Hacker News推了一篇文章我觉得很有意思:I’m a very slow thinker。作者说他反应很慢,经常是别人跟他说一件有趣的事情,他要过一段时间后才反应过来,“噢,原来这么有趣呀,哈哈”;别人问他一个深度问题,他会直接说他不知道,到第二天早上他会给出一个答案来。作者有个不错的观点是,他并不以此为耻,因为他觉得他并不是要去赢得一个辩论比赛,他习惯慢思考,只有慢思考才能真正把握问题的真正奥义,第一反应往往是不准确而且是过时的。 想想我自己,我自认为也是一个反应很慢的人,我根本没法跟那些思维极其敏捷的人相比。比如有些同事,你跟他说一个技术问题,他可以很快就能懂,并且立马给出一个解决方案,我不行,我经常要下来慢慢地从源头思考,才能有机会找到答案,我经常为此而烦恼。后来想想,我的学习曲线前期是非常慢和平缓的,但越到后期,曲线就能越陡,也不见得是一件坏事。 作者的网站还有好多他访问一些人的音频和文本,用来学英文非常好。…

写一个简单的start/stop脚本

最近要写很多脚本,比如start/stop一些应用的。下面就是我刚写的一个start/stop一个Java应用的例子:…

记一次类污染问题定位

今天项目线上测试出现了一个问题,一个War包启动失败(使用Jetty Runner启动),报的错是: Caused by: java.lang.NoSuchMethodError: org.apache.cxf.transport.AbstractTransportFactory.<init>(Ljava/util/List;Lorg/apache/cxf/Bus;)V 这个我一看第一反应是肯定是某个依赖的jar包的版本弄错了。我看了一下AbstractTransportFactory这个类,是属于cxf-api里面的(我们用的版本是2.6.2), 于是我就去Jetty解压出来的war包的目录下的lib里面看,发现里面有,cxf-api(2.6.2), cxf-core(3.1.12)等几个jar包,那就奇怪了,cxf-api的版本是对的,我甚至把它解压出来反编译了AbstractTransportFactory类来看,也是没有问题的。百思不得其解。后来同事Jun Gong提醒我说既然它报了那个错,…

用shade插件build可执行jar包

之前一直用assembly插件来build可执行的jar包,但用assembly插件有个问题,就是在build有依赖到Spring Framework的jar的时候,像META-INF目录下面的如spring.handlers和spring.schemas文件就会被互相覆盖,初了Spring Framework,我们常用的SPI,如果有两个SPI的名字一样(当然,这是不好的practice),也就是说如果META-INF/services目录有两个名字一样的文件,也会被互相覆盖。这个时候,用shade插件就有办法避免。 Shade插件如下: <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.4.1</version> <executions&…

Spring MVC源码剖析系列(2) - DispatcherServlet工作原理之HandlerMapping

在本系列的第一篇,我剖析了DispatcherServlet的初始化过程,但还有很多东西没有说清楚,比如HandlerMapping的具体初始化,本篇就来厘清它。 如果你没有显式指定(通过xml配置文件或者Annotation)你要用的HandlerMapping类的话,DispatcherServlet默认会去classpath底下找DispatcherServlet.properties这个文件,然后从这个文件中读取出HandlerMapping类,并实例化对象。 DispatcherServlet.properties文件如下:(这里用的是4.3.9.release版本的Spring-MVC,里面的那个DefaultAnnotationHandlerMapping已经被Deprecated了,更新的版本是用的RequestMappingHandlerMapping,但这并不影响我们的分析): 我们先看一下DefaultAnnotationHandlerMapping类的继承关系: 我们看到这个DefaultAnnotationHandlerMapping最后其实是实现了ApplicationContextAware这个接口,我们要记住这个,因为它在后面会发挥重要的作用。 我们这里关注的是,究竟DefaultAnnotationHandlerMapping什么时候去将Request与Handler的Mapping关系初始化。在DefaultAnnotationHandlerMapping类里面,没有看到有这个过程,在它的父类AbstractDetectingUrlHandlerMapping里面看到了initApplicationContext()这个方法,看上去是我们要找的: 但这个initApplicationContext方法究竟是什么时候被调用的呢? 我们再回过头来看一下DefaultAnnotationHandlerMapping对象是怎样创建出来的: 我们可以看到它是调到了Spring的AbstractAutowireCapableBeanFactory的createBean方法来创建对象: 到这里,调用了doCreateBean方法,这个方法比较长,我们直奔主题吧,秘密就藏在doCreateBean方法里面调用的initializeBean方法里面(从名字已经能看出,它是负责初始化的): 这个方法也比较枯燥,我们要看的是它里面调用的applyBeanPostProcessorsBeforeInitialization方法: 我们可以看到这个方法是一一调用ApplicationContext里面的所有BeanPostProcessor的postProcessorBeforeInitialization 方法,这些BeanPostProcessor都是在AbstractApplicationContext的refresh方法里面通过调用prepareBeanFactory方法注册进来的: 其中我们要关注的是ApplicationContextAwareProcessor,我们看一下它的postProcessBeforeInitialization方法: 再看它里面调用的invokeAwareInterfaces方法:…

Building the Twelve-Factor microservice service application

A successful microservice architecture requires strong application development and DevOps practices. One of the most succinct summaries of these practices can be found in Heroku's Twelve-Factor Application manifesto. This document provides 12 best practices you should always keep in the back of your mind when building microservices. I've summarized them…

Jetty源码剖析系列(7) - 底层网络通信的细节

我们先来回顾一下Jetty负责网络连接的类ServerConnector的构造函数: 可以看到在ServerConnector的构造函数里会创建一个SelectorManager,然后加到它的bean里面(这里其实体现了Jetty的一个设计模式,就是设计了一个containner,把相关的模块放到这个containner里面,启动该container(调用doStart方法)就会连带启动里面的模块(调用模块的doStart方法), Jetty里面的Server,Connector都是这样的containner。) 我们再来看newSelectorManager方法: 就是创建一个ServerConnectorManager对象,ServerConnectorManager继承了SelectorManager类: ,我们再看一下SelectorManager的doStart方法: 可以看到在调用父类的doStart方法之前,会创建ManagedSelector,并将该selector加到SelectorManager的bean里面,随后再在调用父类的doStart方法时调用该selector的doStart方法,注意selector的数目是根据CPU的数量计算出来的, newSelector方法其实就是直接构建一个ManagedSelector对象: 我们看一下ManagedSelector的构造函数: 关于ManagedSelector,我在系列(4)里面已经有详细剖析。 此时我们先回过头去看一下ServerConnector的doStart方法: 它先调用open方法打开ServerSocketChannel,这个我在系列(6)里面有详细剖析。我们这里关注一下它调用的父类的doStart(AbstractConnector)方法: 它先调用了父类ConntainnerLifeCycle的doStart方法将它所拥有的bean(模块)都启动起来,其中就包括了我们前面加进去的SelectorManager,随即也就会把SelectorManager里面的ManagedSelector启动起来(调用它的doStart方法)。到此,我们可以看到ServerConnector会先启动Selector,然后再启动Acceptor。Connector,Selector,Acceptor,这三者的关系我画了下图来说明: 从上图可以看到,Acceptor负责接收网络连接,…

Spring MVC源码剖析系列(1) - DispatcherServlet工作原理之初始化

最近接手一个项目,用的是SpringMVC框架。循例想剖析一下它的源码,以求更深刻地理解MVC的实现。 DispatcherServlet是SpringMVC最核心的类,就拿它开刀:) 在web.xml中定义DispatcherServlet和它的Mapping: DispatcherServlet继承于FrameworkServlet,而FrameworkServlet又继承于HttpServletBean, HttpServletBean再继承于HttpServlet: DispatcherServlet有一段静态代码块值得关注: 它会到ClassPath下加载一个叫DispatcherServlet.properties的文件的内容,在SpringMVC的jar包里面有这个文件: 你可以理解为SpringMVC为你预先定义好一些后面有可能会用到的类。 Servlet容器(如Jetty)会调用Servlet的init方法,在这里,HttpServletBean重写了init()方法如下: 上面方法中值得注意的是调用了initServletBean方法,这个方法在FrameworkServlet中被重写了: 我们看到这里将会通过调用initWebApplicationContext方法来初始化一个webApplicationContext,这个非常重要,MVC的所有类都是在这个Context里面被加载进来的,我们继续往下看是如何初始化这个Context的: 我们看到它先尝试去找一个rootContext,这个rootContext有点tricky,如果你在web.xml里面指定以下listener: 那它ContextLoaderListener就会先去初始化一个Context(通过它的contextInitialized方法),并把这个context存储在ServletContext的attribute里面,名字为: 轮到FrameworkServlet初始化Context的时候,就会把它从ServletContext中取出来,作为parent context传给FrameworkServlet要初始化的Context,让它能够拿到之前已经加载的Spring Bean: 在创建Context的时候,先去拿到Context的class对象, 因为我们并没有在web.xml里面给DispatcherServlet配置任何参数,所以就会用默认的Context Class: XmlWebApplicationContext, 是从xml文件来构建这个context。…

Jetty源码剖析系列(6) - Connector与ServerSocket

本文主要从源代码的角度来分析Jetty的Connector如何通过ServerSocket来绑定和监听网络地址和端口的过程。 Jetty的Connector实现类是ServerConnector,循例从它的doStart()方法开始(至于是如何到达这个方法,请移步本系列的前几篇): 它的doStart方法先调用父类AbstractNetworkConnector的doStart方法: 在父类的doStart方法里面会调用子类实现的open方法,此处是ServerConnector的open方法: 到这里已经可以看到Jetty的Connector与Java NIO的关系了,将会调用openAcceptChannel方法来构建一个NIO的ServerSocketChannel: 从上图的代码我们可以看到,Jetty默认用Java NIO来实现了它的网络框架,另外一点就是,如果你用java来实现一套网络框架,无非就是BIO和NIO两种,返朴归真。 先常规调用ServerSocketChannel的静态方法open方法来打开一个ServerSocketChannel: 因为Java的NIO是基于Selector来实现的,所以在这里会通过SelectorProvider类的静态方法provider来加载Selector的实现类: 它先尝试从System Property里加载Selector的实现类并实例化: 如果没有的话,就尝试通过SPI的方式来加载Selector的实现类并实例化: 如果还是没有的话,就用Default的Selector,这个是根据底层操作系统来决定,比如,在Windows系统里将会是WindowsSelectorProvider这个类: WindowsSelectorProvider继承于SelectorProviderImpl这个抽象类, 它的openServerSocketChannel方法将会返回一个新建出来的ServerSocketChannelImpl对象: 到此我们再回过头看ServerConnector的openAcceptChannel方法, 上图通过ServerSocketChannel.open()方法拿到的就是一个ServerSocketChannelImpl实例对象, 我们接着往下看, 如果你没在启动参数里加--host参数,那它就会去指定所谓的全零地址: 那这个所谓的anyLocalAddress又是怎么来的呢? 这里impl是InetAddressImpl,它是在加载InetAddress类的时候,通过它的类静态块初始化的: 而InetAddressImplFactory会通过一个本地方法isIPv6Supported来判断底层操作系统是否支持IPv6,来加载InetAddressImpl的实现类: 在我的windows系统里,是支持IPv6的,所以返回的InetAddressImpl应该是Inet6AddressImpl对象,再来看它的anyLocalAddress方法:…