【Tomcat学习笔记】5-各种Container的toString方法

在 Debug 源码的时候发现日志打得特别清晰,容器之间的关系特别清晰,都是这样的形式

1
StandardEngine[xxx].StandardHost[xxx].StandardContext[xxx].StandardWrapper[xxx]

看了toString方法后,原来如此。

靠,这 TM 也要写篇博客?这么水?哦,我只是觉得我自己专门去看过这块代码,觉得很有印象。

StandardEngine#toString

1
2
3
4
5
6
public String toString() {
StringBuilder sb = new StringBuilder("StandardEngine[");
sb.append(getName());
sb.append("]");
return (sb.toString());
}

【Tomcat学习笔记】4-启动流程分析

讲真,假如你不幸看到这里了,别往下看了,写得一坨 Shit. 只有我自己能看懂的笔记。讲道理,这种流程,真是一图剩千言,然。。。画图好累的。

1. 执行 Bootstrap 类的 static 代码块

主要功能是确定 catalinaHome 和 catalinaBase 两个目录,如果有配置 “catalina.home”/“catalina.base” 这两个 property,就以配置的为准,没有配置就走 fall back,用默认的目录。那么 home 和 base 分别只哪两个目录呢,是用来干嘛的呢。先来看下官方的解释:

  • catalina.home : the tomcat product installation path
  • catalina.base : the tomcat instance installation path
    看了是不是还是一脸闷逼?我也是。OK,看下我们的工程,里面有个目录

【Tomcat学习笔记】2-整体架构

Tomcat的整体架构其实和 server.xml 这个配置文件是可以对应起来的。这是一个最简单的但是能用的 server.xml

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
<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>

<Service name="Catalina">

<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>

<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">

<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b" />

</Host>
</Engine>
</Service>
</Server>

下面是Tomcat的整体架构

【Tomcat学习笔记】1-源码Debug

最近一段时间看了一遍 Tomcat 8.0 的源代码,略有所得,打算整理一个系列的学习笔记。
Tomcat 源码的工程不是用 Maven 或 Gradle 管理的,使用起来不方便,Github 上有人整理一个 Maven 的工程。我 Fork 了一份

工程的整体结构:

1
2
3
4
5
6
7
8
9
10
11
12
tomcat8.0-source-research
catalina-home
jsp-demo
PDF-resource
springmvc-demo
tomcat-8.0-doc
tomcat-code
.gitignore
NOTES.md
pom.xml
README.md
tomcat-study.iml
  • 只有 tomcat-code(源码) 和 catalina-home (配置、lib等)是运行工程必须的

Java 锁 学习笔记

纯粹个人笔记,为什么要写博客?强化自己学习的知识点。实践过的东西才最深刻,但很多知识平时项目中不一定有机会实践,所以唯有自己能够写下来、描述清楚才是真的理解了。

最近要做个Java锁的分享,看了些资料,做了个slide. 这里再对这些东西做个总结。

锁是一种多线程间的同步工具,最简单的例子,两个线程同时执行1000次count++操作(count是int形的),最后的结果并不一定是2000. 因为count++并不是一个原子操作。

JDK定义了一个标准的Lock接口,如下:

1
2
3
4
5
6
7
8
9
10
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
//如果lock成功返回true, 否则返回false,不会阻塞线程
boolean tryLock();
//如果在一段时间内lock成功返回true, 返回false
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}

Java里有两种类型的锁,一种是synchronized,一种是实现了Lock接口的锁。原理上,前者是在JVM层面做的,后者更多的是在Java层面做的,所以后者也更容易在应用层做扩展,更容易通过JDK源码去理解它的机制。

Java Volatile 学习笔记

纯粹个人笔记,为什么要写博客?强化自己学习的知识点。实践过的东西才最深刻,但很多知识平时项目中不一定有机会实践,所以唯有自己能够写下来、描述清楚才是真的理解了。

CPU的计算性能已经远远超于内存的访问性能,所以芯片设计上用了很多手段来减少他们之间的gap,最主要的办法还是加了多级缓存, 如图所示:

aaa
图片来源 http://mechanical-sympathy.blogspot.com

Java内存模型也类似,下面是一个更加抽象的图:
bbb
图片来源 http://tech.meituan.com/

这个模型存在一个问题,当多个线程共享主内存中的一个数据时,各个CPU的缓存有可能是不一样的,存在数据不一致的问题。

Netty学习笔记

最近看了一些源码(netty-4.1.0.Final),结合李林锋的文章学习了一下Netty.
先看几个基本概念,BIO | NIO | AIO

1. IO

BIO, 同步阻塞式IO

一个连接一个线程,数据的读写是阻塞的,可以通过线程池来避免频繁的创建和销毁线程。

NIO, 同步非阻塞式IO

所有的连接都会注册到一个多路复用器(selector)上,然后有个线程不停的轮询这个selector,有数据可读时才创建或使用线程池中的线程去处理该数据。

NIO基于Reactor,当socket有流可读或可写入socket时,操作系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操作系统。这个时候,已经不是一个连接对应一个线程了,而是 一个有效的请求对应一个线程,当连接没有数据时,是没有工作线程来处理的。