Netty,IO模型

pull/28/head
AmyliaY 6 years ago
parent 49b7bef419
commit 57fa2deb8f

@ -83,11 +83,29 @@
## Netty ## Netty
### IO ### 网络 IO 技术基础
- [把被说烂的 BIO、NIO、AIO 再从头到尾扯一遍](docs/Netty/IO/把被说烂的BIO、NIO、AIO再从头到尾扯一遍.md) - [把被说烂的 BIO、NIO、AIO 再从头到尾扯一遍](docs/Netty/IOTechnologyBase/把被说烂的BIO、NIO、AIO再从头到尾扯一遍.md)
- [IO模型](docs/Netty/IOTechnologyBase/IO模型.md)
- [四种IO编程及对比](docs/Netty/IOTechnologyBase/四种IO编程及对比.md)
### 设计原理 ### Netty NIO开发指南
- 努力编写中... - [TCP粘包/拆包问题的解决之道]()
- [分隔符解码器 和 定长解码器]()
### Netty 编解码开发指南
- [编解码技术]()
### Netty 多协议开发和应用
- [WebSocket 协议开发]()
### Netty 源码分析
- [Channel和Unsafe组件]()
- [ChannelPipeline和ChannelHandler组件]()
- [EventLoop和EventLoopGroup组件]()
### Netty高级特性
- [Java多线程编程在Netty中的应用]()
- [Netty的高性能之道]()
## Redis ## Redis
- 努力编写中... - 努力编写中...

@ -111,7 +111,7 @@ public class CustomReflectorFactory extends DefaultReflectorFactory {
} }
``` ```
### 1.3 ObjectFactory ### 1.3 ObjectFactory
该类也是接口+一个默认实现类并且支持自定义扩展mybatis中有很多这样的设计方式。 该类也是接口加一个默认实现类并且支持自定义扩展Mybatis 中有很多这样的设计方式。
```java ```java
/** /**
* MyBatis uses an ObjectFactory to create all needed new Objects. * MyBatis uses an ObjectFactory to create all needed new Objects.
@ -145,7 +145,7 @@ public interface ObjectFactory {
/** /**
* ObjectFactory接口 的唯一直接实现,反射工厂,根据传入的参数列表,选择 * ObjectFactory接口 的唯一直接实现,反射工厂,根据传入的参数列表,选择
* 合适的构造函数实例化对象,不传参数,则直接调用其午餐构造方法 * 合适的构造函数实例化对象,不传参数,则直接调用其无参构造方法
*/ */
public class DefaultObjectFactory implements ObjectFactory, Serializable { public class DefaultObjectFactory implements ObjectFactory, Serializable {
@ -206,9 +206,9 @@ public class DefaultObjectFactory implements ObjectFactory, Serializable {
} }
``` ```
## 2 类型转换 ## 2 类型转换
类型转换是实现ORM的重要一环由于 数据库中的数据类型与Java语言的数据类型并不对等所以在PrepareStatement为sql语句绑定参数时需要从Java类型转换成JDBC类型而从结果集获取数据时又要将JDBC类型转换成Java类型mybatis使用TypeHandler完成了上述的双向转换。 类型转换是实现 ORM 的重要一环,由于数据库中的数据类型与 Java语言 的数据类型并不对等,所以在 PrepareStatement sql语句 绑定参数时,需要从 Java类型 转换成 JDBC类型而从结果集获取数据时又要将 JDBC类型 转换成 Java类型Mybatis 使用 TypeHandler 完成了上述的双向转换。
### 2.1 JdbcType ### 2.1 JdbcType
mybatis通过JdbcType这个枚举类型代表了JDBC中的数据类型 Mybatis 通过 JdbcType 这个枚举类型代表了 JDBC 中的数据类型。
```java ```java
/** /**
* 该枚举类描述了 JDBC 中的数据类型 * 该枚举类描述了 JDBC 中的数据类型
@ -282,7 +282,7 @@ public enum JdbcType {
} }
``` ```
### 2.2 TypeHandler ### 2.2 TypeHandler
TypeHandler是mybatis中所有类型转换器的顶层接口,主要用于实现 数据从Java类型到JdbcType类型的相互转换。 TypeHandler 是 Mybatis 中所有类型转换器的顶层接口,主要用于实现数据从 Java类型 JdbcType类型 的相互转换。
```java ```java
public interface TypeHandler<T> { public interface TypeHandler<T> {
@ -344,13 +344,13 @@ public abstract class BaseTypeHandler<T> extends TypeReference<T> implements Typ
public class IntegerTypeHandler extends BaseTypeHandler<Integer> { public class IntegerTypeHandler extends BaseTypeHandler<Integer> {
/** /**
1. NonNull就是NoneNull非空的意思 * NonNull 就是 NoneNull非空的意思
*/ */
@Override @Override
public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType) public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
throws SQLException { throws SQLException {
// IntegerTypeHandler就调用PreparedStatement的setInt方法 // IntegerTypeHandler 就调用 PreparedStatement setInt()方法
// BooleanTypeHandler就调用PreparedStatement的setBoolean方法 // BooleanTypeHandler 就调用 PreparedStatement setBoolean()方法
// 其它的基本数据类型,以此类推 // 其它的基本数据类型,以此类推
ps.setInt(i, parameter); ps.setInt(i, parameter);
} }
@ -379,9 +379,9 @@ public class IntegerTypeHandler extends BaseTypeHandler<Integer> {
``` ```
TypeHandler 主要用于单个参数的类型转换,如果要将多个列的值转换成一个 Java对象可以在映射文件中定义合适的映射规则 &lt;resultMap&gt; 完成映射。 TypeHandler 主要用于单个参数的类型转换,如果要将多个列的值转换成一个 Java对象可以在映射文件中定义合适的映射规则 &lt;resultMap&gt; 完成映射。
### 2.3 TypeHandlerRegistry ### 2.3 TypeHandlerRegistry
TypeHandlerRegistry主要负责管理所有已知的TypeHandlermybatis在初始化过程中会为所有已知的TypeHandler创建对象并注册到TypeHandlerRegistry。 TypeHandlerRegistry 主要负责管理所有已知的 TypeHandlerMybatis 在初始化过程中会为所有已知的 TypeHandler 创建对象,并注册到 TypeHandlerRegistry。
```java ```java
//TypeHandlerRegistry中的核心字段 // TypeHandlerRegistry 中的核心字段如下
/** 该集合主要用于从结果集读取数据时,将数据从 JDBC类型 转换成 Java类型 */ /** 该集合主要用于从结果集读取数据时,将数据从 JDBC类型 转换成 Java类型 */
private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class); private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
@ -392,14 +392,14 @@ TypeHandlerRegistry主要负责管理所有已知的TypeHandlermybatis在初
*/ */
private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>(); private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
/** key TypeHandler的类型value 该TypeHandler类型对应的TypeHandler对象 */ /** keyTypeHandler 的类型value该 TypeHandler类型 对应的 TypeHandler对象 */
private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>(); private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();
``` ```
1. **注册TypeHandler对象** **1、注册TypeHandler对象**
TypeHandlerRegistry 中的 register()方法 实现了注册 TypeHandler对象 的功能,该方法存在多种重载,但大多数 register()方法 最终都会走 register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) 的处理逻辑,该重载方法中分别指定了 TypeHandler 能够处理的 Java类型、JDBC类型、TypeHandler对象。 TypeHandlerRegistry 中的 register()方法 实现了注册 TypeHandler对象 的功能,该方法存在多种重载,但大多数 register()方法 最终都会走 register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) 的处理逻辑,该重载方法中分别指定了 TypeHandler 能够处理的 Java类型、JDBC类型、TypeHandler对象。
```java ```java
/** /**
* TypeHandlerRegistry中对register方法实现了多种重载本register方法 * TypeHandlerRegistry 中对 register()方法 实现了多种重载,本 register()方法
* 被很多重载方法调用,用来完成注册功能。 * 被很多重载方法调用,用来完成注册功能。
*/ */
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) { private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
@ -436,10 +436,10 @@ TypeHandlerRegistry中的register()方法实现了注册TypeHandler对象的功
```java ```java
/** /**
* 进行 Java 及 JDBC基本数据类型 的 TypeHandler 注册 * 进行 Java 及 JDBC基本数据类型 的 TypeHandler 注册
* 除了注册mybatis提供的基本TypeHandler外我们也可以添加自定义的TypeHandler * 除了注册 Mybatis 提供的 基本TypeHandler 外,我们也可以添加自定义的 TypeHandler
* 接口实现,在 mybatis-config.xml配置文件 中 <typeHandlers>节点 下添加相应的 * 接口实现,在 mybatis-config.xml配置文件 中 <typeHandlers>节点 下添加相应的
* <typeHandlers>节点配置,并指定自定义的TypeHandler实现类。mybatis在初始化时 * <typeHandlers>节点配置,并指定自定义的 TypeHandler实现类。Mybatis 在初始化时
* 会解析该节点,并将TypeHandler类型的对象注册到TypeHandlerRegistry中 供mybatis后续使用 * 会解析该节点,并将 TypeHandler类型 的对象注册到 TypeHandlerRegistry 中供 Mybatis 后续使用
*/ */
public TypeHandlerRegistry() { public TypeHandlerRegistry() {
register(Boolean.class, new BooleanTypeHandler()); register(Boolean.class, new BooleanTypeHandler());
@ -515,20 +515,20 @@ TypeHandlerRegistry中的register()方法实现了注册TypeHandler对象的功
register(JapaneseDate.class, new JapaneseDateTypeHandler()); register(JapaneseDate.class, new JapaneseDateTypeHandler());
} }
``` ```
2. **查找TypeHandler** **2、查找TypeHandler**
TypeHandlerRegistry其实就是一个容器前面注册了一堆东西也就是为了方便获取其对应的方法为getTypeHandler(),也存在多种重载其中最重要的一个重载为getTypeHandler(Type type, JdbcType jdbcType)它会根据指定的Java类型和JdbcType类型查找相应的TypeHandler对象。 TypeHandlerRegistry 其实就是一个容器,前面注册了一堆东西,也就是为了方便获取,其对应的方法为 getTypeHandler()该方法也存在多种重载,其中最重要的一个重载为 getTypeHandler(Type type, JdbcType jdbcType),它会根据指定的 Java类型 JdbcType类型 查找相应的 TypeHandler对象。
```java ```java
/** /**
* 获取 TypeHandler对象 * 获取 TypeHandler对象
* getTypeHandler方法亦存在多种重载而本重载方法被其它多个重载方法调用 * getTypeHandler()方法 亦存在多种重载,而本重载方法被其它多个重载方法调用
*/ */
private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) { private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
if (ParamMap.class.equals(type)) { if (ParamMap.class.equals(type)) {
return null; return null;
} }
// Java数据类型 与 JDBC数据类型 的关系往往是一对多, // Java数据类型 与 JDBC数据类型 的关系往往是一对多,
// 所以一般会先根据Java数据类型获取Map<JdbcType, TypeHandler<?>> // 所以一般会先根据 Java数据类型 获取 Map<JdbcType, TypeHandler<?>>对象
// 再根据JDBC数据类型获取对应的TypeHandler // 再根据 JDBC数据类型 获取对应的 TypeHandler对象
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type); Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
TypeHandler<?> handler = null; TypeHandler<?> handler = null;
if (jdbcHandlerMap != null) { if (jdbcHandlerMap != null) {
@ -545,4 +545,4 @@ TypeHandlerRegistry其实就是一个容器前面注册了一堆东西
return (TypeHandler<T>) handler; return (TypeHandler<T>) handler;
} }
``` ```
除了mabatis本身自带的TypeHandler实现我们还可以添加自定义的TypeHandler实现类在配置文件mybatis-config.xml中的&lt;typeHandler&gt;标签下配置好自定义TypeHandlermybatis就会在初始化时解析该标签内容完成自定义TypeHandler的注册。 除了 Mabatis 本身自带的 TypeHandler实现我们还可以添加自定义的 TypeHandler实现类在配置文件 mybatis-config.xml 中的 &lt;typeHandler&gt; 标签下配置好 自定义TypeHandlerMybatis 就会在初始化时解析该标签内容,完成 自定义TypeHandler 的注册。

@ -1,6 +1,6 @@
在数据持久层,数据源和事务是两个非常重要的组件,对数据持久层的影响很大,在实际开发中,一般会使用mybatis集成第三方数据源组件c3p0、Druid另外mybatis也提供了自己的数据库连接池实现本文会通过mybatis的源码实现来了解数据库连接池的设计。而事务方面一般使用spring进行事务的管理这里不做详细分析。下面我们看一下mybatis是如何对这两部分进行封装的。 在数据持久层,数据源和事务是两个非常重要的组件,对数据持久层的影响很大,在实际开发中,一般会使用 Mybatis 集成第三方数据源组件c3p0、Druid另外Mybatis 也提供了自己的数据库连接池实现,本文会通过 Mybatis 的源码实现来了解数据库连接池的设计。而事务方面,一般使用 Spring 进行事务的管理,这里不做详细分析。下面我们看一下 Mybatis 是如何对这两部分进行封装的。
## 1 DataSource ## 1 DataSource
常见的数据源都会实现javax.sql.DataSource接口mybatis中提供了两个该接口的实现类分别是PooledDataSource和UnpooledDataSource并使用不同的工厂类分别管理这两个类的对象。 常见的数据源都会实现 javax.sql.DataSource接口Mybatis 中提供了两个该接口的实现类分别是PooledDataSource UnpooledDataSource并使用不同的工厂类分别管理这两个类的对象。
### 1.1 DataSourceFactory ### 1.1 DataSourceFactory
DataSourceFactory系列类 的设计比较简单DataSourceFactory 作为顶级接口UnpooledDataSourceFactory 实现了该接口PooledDataSourceFactory 又继承了 UnpooledDataSourceFactory。 DataSourceFactory系列类 的设计比较简单DataSourceFactory 作为顶级接口UnpooledDataSourceFactory 实现了该接口PooledDataSourceFactory 又继承了 UnpooledDataSourceFactory。
```java ```java
@ -101,7 +101,7 @@ public class UnpooledDataSource implements DataSource {
/** /**
* UnpooledDataSource 被加载时,会通过该静态代码块将已经在 DriverManager * UnpooledDataSource 被加载时,会通过该静态代码块将已经在 DriverManager
* 中注册JDBC Driver复制一份到registeredDrivers * 中注册的 JDBC Driver 注册到 registeredDrivers 中
*/ */
static { static {
Enumeration<Driver> drivers = DriverManager.getDrivers(); Enumeration<Driver> drivers = DriverManager.getDrivers();
@ -198,7 +198,7 @@ class PooledConnection implements InvocationHandler {
/** /**
* invoke()方法 是本类的重点实现,也是 proxyConnection代理连接对象 的代理逻辑实现 * invoke()方法 是本类的重点实现,也是 proxyConnection代理连接对象 的代理逻辑实现
* 它会对close()方法的调用进行处理并在调用realConnection的方法之前进行校验 * 它会对 close()方法 的调用进行处理,并在调用 realConnection对象 的方法之前进行校验
*/ */
@Override @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
@ -213,7 +213,7 @@ class PooledConnection implements InvocationHandler {
// 通过上面的 valid字段 校验连接是否有效 // 通过上面的 valid字段 校验连接是否有效
checkConnection(); checkConnection();
} }
// 调用realConnection的对应方法 // 调用 realConnection对象 的对应方法
return method.invoke(realConnection, args); return method.invoke(realConnection, args);
} catch (Throwable t) { } catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t); throw ExceptionUtil.unwrapThrowable(t);
@ -540,7 +540,8 @@ public class PooledDataSource implements DataSource {
state.activeConnections.remove(conn); state.activeConnections.remove(conn);
// 如果该连接有效 // 如果该连接有效
if (conn.isValid()) { if (conn.isValid()) {
// 如果连接池中的空闲连接数未达到阈值 且 该连接确实属于本连接池通过之前获取的expectedConnectionTypeCode进行校验 // 如果连接池中的空闲连接数未达到阈值 且 该连接确实属于
// 本连接池(通过之前获取的 expectedConnectionTypeCode 进行校验)
if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) { if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
// CheckoutTime = 应用从连接池取出连接到归还连接的时长 // CheckoutTime = 应用从连接池取出连接到归还连接的时长
// accumulatedCheckoutTime = 所有连接累计的CheckoutTime // accumulatedCheckoutTime = 所有连接累计的CheckoutTime
@ -703,8 +704,8 @@ public class PooledDataSource implements DataSource {
} }
``` ```
## 2 Transaction ## 2 Transaction
遵循接口-实现类的设计原则mybatis也是先使用Transaction接口对数据库事务做了抽象而实现类则只提供了两个JdbcTransaction和ManagedTransaction。这两种对象的获取使用了两个对应的工厂类JdbcTransactionFactory和ManagedTransactionFactory。 遵循 “接口-实现类” 的设计原则Mybatis 也是先使用 Transaction接口 对数据库事务做了抽象而实现类则只提供了两个JdbcTransaction ManagedTransaction。这两种对象的获取使用了两个对应的工厂类 JdbcTransactionFactory ManagedTransactionFactory。
不过一般我们并不会使用mybatis管理事务而是将mybatis集成到spring由spring进行事务的管理。细节部分会在后面的文章中详细讲解。 不过一般我们并不会使用 Mybatis 管理事务,而是将 Mybatis 集成到 Spring由 Spring 进行事务的管理。细节部分会在后面的文章中详细讲解。
```java ```java
public interface Transaction { public interface Transaction {
@ -866,7 +867,7 @@ public class ManagedTransaction implements Transaction {
// 控制是否关闭持有的连接,在 close()方法 中用其判断是否真的关闭连接 // 控制是否关闭持有的连接,在 close()方法 中用其判断是否真的关闭连接
private final boolean closeConnection; private final boolean closeConnection;
// 本类的实现也很简单commit、rollback方法都是空实现 // 本类的实现也很简单commit()、rollback()方法 都是空实现
public ManagedTransaction(Connection connection, boolean closeConnection) { public ManagedTransaction(Connection connection, boolean closeConnection) {
this.connection = connection; this.connection = connection;
this.closeConnection = closeConnection; this.closeConnection = closeConnection;

@ -1,6 +1,6 @@
binding模块主要为了解决一个历史遗留问题原先查询一个VO对象时需要调用SqlSession.queryForObject(“selectXXVOById”, primaryKey)方法执行指定的sql语句第一个参数selectXXVOById指定了执行的sql语句id如果我们不小心写错了参数mybatis是无法在初始化时发现这个错误的只会在实际调用queryForObject(“selectXXVOById”, primaryKey)方法时才会抛出异常这对于工程师来说是非常难受的就像泛型出来之前很多类型转换不会在编译期发现错误一样。而binding模块就像Java的泛型机制一样将程序的错误提前暴露出来为开发人员省去不少排查问题的精力。 binding模块 主要为了解决一个历史遗留问题,原先查询一个 VO对象 时需要调用 SqlSession.queryForObject(“selectXXVOById”, primaryKey)方法,执行指定的 sql语句第一个参数 selectXXVOById 指定了执行的 sql语句id如果我们不小心写错了参数Mybatis 是无法在初始化时发现这个错误的,只会在实际调用 queryForObject(“selectXXVOById”, primaryKey)方法 时才会抛出异常,这对于工程师来说是非常难受的,就像泛型出来之前,很多类型转换不会在编译期发现错误一样。而 binding模块 就像 Java的泛型机制 一样,将程序的错误提前暴露出来,为开发人员省去不少排查问题的精力。
binding模块的解决方案是定义一个Mapper接口在接口中定义sql语句对应的方法名(Id)及参数这些方法在mybatis的初始化过程中会与该Mapper接口对应的映射配置文件中的sql语句相关联如果存在无法关联的sql语句mybatis就会抛出异常,帮助我们及时发现问题。示例代码如下: binding模块 的解决方案是,定义一个 Mapper接口在接口中定义 sql语句 对应的 方法名Id 及 参数,这些方法在 Mybatis 的初始化过程中,会与该 Mapper接口 对应的映射配置文件中的 sql语句 相关联,如果存在无法关联的 sql语句Mybatis 就会抛出异常,帮助我们及时发现问题。示例代码如下:
```java ```java
public interface HeroMapper { public interface HeroMapper {
// 映射文件中会存在一个 <select>节点id 为 “selectHeroVOById” // 映射文件中会存在一个 <select>节点id 为 “selectHeroVOById”
@ -9,16 +9,17 @@ public interface HeroMapper {
// 首先,获取 HeroMapper 对应的代理对象 // 首先,获取 HeroMapper 对应的代理对象
HeroMapper heroMapper = session.getMapper(HeroMapper.class); HeroMapper heroMapper = session.getMapper(HeroMapper.class);
// 直接调用HeroMapper接口中的方法 获取结果集 // 直接调用 HeroMapper接口 中的方法,获取结果集
HeroVO heroVO = heroMapper.selectHeroVOById("23333"); HeroVO heroVO = heroMapper.selectHeroVOById("23333");
``` ```
## 1 MapperRegistry和MapperProxyFactory ## 1 MapperRegistry和MapperProxyFactory
MapperRegistry是Mapper接口及其对应的代理对象工厂的注册中心。Configuration是mybatis中全局性的配置对象根据mybatis的核心配置文件mybatis-config.xml解析而成。Configuration通过mapperRegistry属性持有该对象。 MapperRegistry 是 Mapper接口 及其对应的代理对象工厂的注册中心。Configuration 是 Mybatis 中全局性的配置对象,根据 Mybatis 的核心配置文件 mybatis-config.xml 解析而成。Configuration 通过 mapperRegistry属性 持有该对象。
mybatis在初始化过程中会读取映射配置文件和Mapper接口中的注解信息并调用MapperRegistry的addMappers()方法填充knownMappers集合在需要执行某sql语句时会先调用getMapper()方法获取实现了Mapper接口的动态代理对象。
Mybatis 在初始化过程中会读取映射配置文件和 Mapper接口 中的注解信息,并调用 MapperRegistry 的 addMappers()方法 填充 knownMappers集合在需要执行某 sql语句 时,会先调用 getMapper()方法 获取实现了 Mapper接口 的动态代理对象。
```java ```java
public class MapperRegistry { public class MapperRegistry {
// mybatis全局唯一的配置对象,包含了几乎所有配置信息 // Mybatis 全局唯一的配置对象,包含了几乎所有配置信息
private final Configuration config; private final Configuration config;
// keyMapper接口valueMapperProxyFactory 为 Mapper接口 创建代理对象的工厂 // keyMapper接口valueMapperProxyFactory 为 Mapper接口 创建代理对象的工厂
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>(); private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
@ -114,7 +115,7 @@ public class MapperProxyFactory<T> {
} }
public T newInstance(SqlSession sqlSession) { public T newInstance(SqlSession sqlSession) {
// 每都会创建一个新的MapperProxy对象 // 每都会创建一个新的 MapperProxy对象
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy); return newInstance(mapperProxy);
} }
@ -169,7 +170,7 @@ public class MapperProxy<T> implements InvocationHandler, Serializable {
// 主要负责维护 methodCache 缓存 // 主要负责维护 methodCache 缓存
private MapperMethod cachedMapperMethod(Method method) { private MapperMethod cachedMapperMethod(Method method) {
// 这里用到了Java8的新特性computeIfAbsent()是Java8的方法Lambda表达式也是Java8中最重要的新特性之一 // 这里用到了 Java8 的新特性computeIfAbsent() Java8 新增的方法Lambda表达式 也是 Java8中 最重要的新特性之一
// computeIfAbsent()方法 表示 当前map中若 key 对应的 value 为空,则执行传入的 Lambda表达式将 key 和表达式的 value // computeIfAbsent()方法 表示 当前map中若 key 对应的 value 为空,则执行传入的 Lambda表达式将 key 和表达式的 value
// 存入 当前map并返回 value值 // 存入 当前map并返回 value值
// 在这段代码中的意思是:若 methodCache 中没有 method 对应的 value就执行右侧的 Lambda表达式并将表达式的结果 // 在这段代码中的意思是:若 methodCache 中没有 method 对应的 value就执行右侧的 Lambda表达式并将表达式的结果
@ -180,6 +181,7 @@ public class MapperProxy<T> implements InvocationHandler, Serializable {
``` ```
## 3 MapperMethod ## 3 MapperMethod
MapperMethod 中封装了 Mapper接口 中对应方法的信息,和对应 sql语句 的信息,是连接 Mapper接口 及映射配置文件中定义的 sql语句 的桥梁。 MapperMethod 中封装了 Mapper接口 中对应方法的信息,和对应 sql语句 的信息,是连接 Mapper接口 及映射配置文件中定义的 sql语句 的桥梁。
MapperMethod 中持有两个非常重要的属性,这两个属性对应的类 都是 MapperMethod 中的静态内部类。另外MapperMethod 在被实例化时就对这两个属性进行了初始化。 MapperMethod 中持有两个非常重要的属性,这两个属性对应的类 都是 MapperMethod 中的静态内部类。另外MapperMethod 在被实例化时就对这两个属性进行了初始化。
```java ```java
public class MapperMethod { public class MapperMethod {
@ -209,7 +211,7 @@ MapperMethod中的核心方法execute()就主要用到了这两个类,所以
final String methodName = method.getName(); final String methodName = method.getName();
// 该方法对应的类的 Class对象 // 该方法对应的类的 Class对象
final Class<?> declaringClass = method.getDeclaringClass(); final Class<?> declaringClass = method.getDeclaringClass();
// MappedStatement封装了sql语句相关的信息在mybatis初始化时创建 // MappedStatement 封装了 sql语句 相关的信息,在 Mybatis初始化 时创建
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass, configuration); MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass, configuration);
if (ms == null) { if (ms == null) {
// 处理 Flush注解 // 处理 Flush注解
@ -289,7 +291,7 @@ MapperMethod中的核心方法execute()就主要用到了这两个类,所以
/** /**
* 顾名思义,这是一个处理 Mapper接口 中 方法参数列表的解析器,它使用了一个 SortedMap<Integer, String> * 顾名思义,这是一个处理 Mapper接口 中 方法参数列表的解析器,它使用了一个 SortedMap<Integer, String>
* 类型的容器 记录了参数在参数列表中的位置索引 与 参数名之间的对应关系key参数在参数列表中的索引位置 * 类型的容器记录了参数在参数列表中的位置索引 与 参数名之间的对应关系key参数 在参数列表中的索引位置,
* value参数名(参数名可用@Param注解指定默认使用参数索引作为其名称) * value参数名(参数名可用@Param注解指定默认使用参数索引作为其名称)
*/ */
private final ParamNameResolver paramNameResolver; private final ParamNameResolver paramNameResolver;
@ -340,7 +342,7 @@ MapperMethod中的核心方法execute()就主要用到了这两个类,所以
} }
``` ```
### 3.3 execute()方法 ### 3.3 execute()方法
execute()方法会根据sql语句的类型(CRUD)调用SqlSession对应的方法完成数据库操作SqlSession是mybatis的核心组件之一,后面会详细解读。 execute()方法 会根据 sql语句 的类型(CRUD)调用 SqlSession 对应的方法完成数据库操作SqlSession 是 Mybatis 的核心组件之一,后面会详细解读。
```java ```java
public class MapperMethod { public class MapperMethod {
public Object execute(SqlSession sqlSession, Object[] args) { public Object execute(SqlSession sqlSession, Object[] args) {
@ -521,7 +523,7 @@ public class MapperMethod {
Object param = method.convertArgsToSqlCommandParam(args); Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) { if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args); RowBounds rowBounds = method.extractRowBounds(args);
// 注意这里调用的是sqlSession的selectMap方法使用返回的是一个Map类型结果集 // 注意这里调用的是 SqlSession 的 selectMap()方法,返回的是一个 Map类型结果集
result = sqlSession.selectMap(command.getName(), param, method.getMapKey(), rowBounds); result = sqlSession.selectMap(command.getName(), param, method.getMapKey(), rowBounds);
} else { } else {
result = sqlSession.selectMap(command.getName(), param, method.getMapKey()); result = sqlSession.selectMap(command.getName(), param, method.getMapKey());

@ -1,4 +1,4 @@
MyBatis中的缓存分为一级缓存、二级缓存但在本质上是相同的它们使用的都是Cache接口的实现。MyBatis缓存模块的设计 使用了装饰器模式,这里不对此进行过多解析,以后会专门开一篇博文分析常用框架中使用到的设计模式。 MyBatis 中的缓存分为一级缓存、二级缓存,但在本质上是相同的,它们使用的都是 Cache接口 的实现。MyBatis缓存模块 的设计,使用了装饰器模式,这里不对此进行过多解析,以后会专门开一篇博文分析常用框架中使用到的设计模式。
## 1 Cache组件 ## 1 Cache组件
MyBatis 中缓存模块相关的代码位于 org.apache.ibatis.cache包 下,其中 Cache接口 是缓存模块中最核心的接口,它定义了所有缓存的基本行为。 MyBatis 中缓存模块相关的代码位于 org.apache.ibatis.cache包 下,其中 Cache接口 是缓存模块中最核心的接口,它定义了所有缓存的基本行为。
```java ```java
@ -48,6 +48,7 @@ public interface Cache {
如下图所示Cache接口 的实现类有很多,但大部分都是装饰器,只有 PerpetualCache 提供了 Cache 接口 的基本实现。 如下图所示Cache接口 的实现类有很多,但大部分都是装饰器,只有 PerpetualCache 提供了 Cache 接口 的基本实现。
![avatar](/images/mybatis/Cache组件.png) ![avatar](/images/mybatis/Cache组件.png)
### 1.1 PerpetualCache ### 1.1 PerpetualCache
PerpetualCachePerpetual永恒的持续的在缓存模块中扮演着被装饰的角色其实现比较简单底层使用 HashMap 记录缓存项,也是通过该 HashMap对象 的方法实现的 Cache接口 中定义的相应方法。 PerpetualCachePerpetual永恒的持续的在缓存模块中扮演着被装饰的角色其实现比较简单底层使用 HashMap 记录缓存项,也是通过该 HashMap对象 的方法实现的 Cache接口 中定义的相应方法。
```java ```java
@ -141,7 +142,7 @@ public class BlockingCache implements Cache {
} }
} }
``` ```
假设线程A在BlockingCache中未查找到keyA对应的缓存项时线程A会获取keyA对应的锁这样后续线程A在查找keyA时,其它线程会被阻塞。 假设 线程A 在 BlockingCache 中未查找到 keyA 对应的缓存项时线程A 会获取 keyA 对应的锁这样线程A 在后续查找 keyA 时,其它线程会被阻塞。
```java ```java
// 根据 key 获取锁对象,然后上锁 // 根据 key 获取锁对象,然后上锁
private void acquireLock(Object key) { private void acquireLock(Object key) {

@ -1,6 +1,6 @@
spring框架的IoC容器初始化一样mybatis也会通过定位、解析相应的配置文件完成自己的初始化。mybatis的配置文件主要有mybatis-config.xml核心配置文件及一系列映射配置文件另外mybatis也会根据注解进行配置。 Spring框架 的 IoC容器初始化 一样Mybatis 也会通过定位、解析相应的配置文件完成自己的初始化。Mybatis 的配置文件主要有 mybatis-config.xml核心配置文件 及一系列映射配置文件另外Mybatis 也会根据注解进行配置。
## 1 BaseBuilder ## 1 BaseBuilder
mybatis初始化的主要内容是加载并解析mybatis-config.xml配置文件、映射配置文件以及相关的注解信息。mybatis的初始化入口是SqlSessionFactoryBuilder的build()方法。 Mybatis初始化 的主要内容是加载并解析 mybatis-config.xml配置文件、映射配置文件以及相关的注解信息。Mybatis 的初始化入口是 SqlSessionFactoryBuilder 的 build()方法。
```java ```java
public class SqlSessionFactoryBuilder { public class SqlSessionFactoryBuilder {
@ -17,14 +17,14 @@ public class SqlSessionFactoryBuilder {
} }
/** /**
* build方法的主要实现 * build()方法 的主要实现
*/ */
public SqlSessionFactory build(Reader reader, String environment, Properties properties) { public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try { try {
// SqlSessionFactory 会创建 XMLConfigBuilder对象 来解析 mybatis-config.xml配置文件 // SqlSessionFactory 会创建 XMLConfigBuilder对象 来解析 mybatis-config.xml配置文件
// XMLConfigBuilder 继承自 BaseBuilder抽象类顾名思义这一系的类使用了 建造者设计模式 // XMLConfigBuilder 继承自 BaseBuilder抽象类顾名思义这一系的类使用了 建造者设计模式
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
// 解析配置文件的内容 到Configuration对象根据到Configuration对象 // 解析配置文件的内容 到 Configuration对象根据 Configuration对象
// 创建 DefaultSqlSessionFactory对象然后返回 // 创建 DefaultSqlSessionFactory对象然后返回
return build(parser.parse()); return build(parser.parse());
} catch (Exception e) { } catch (Exception e) {
@ -48,7 +48,7 @@ BaseBuilder中的核心字段如下
```java ```java
public abstract class BaseBuilder { public abstract class BaseBuilder {
// 保存了mybatis的几乎所以核心配置信息,全局唯一 // 保存了 Mybatis 的几乎所以核心配置信息,全局唯一
protected final Configuration configuration; protected final Configuration configuration;
// 在 mybatis-config.xml 中可以通过 <typeAliases>标签 定义别名 // 在 mybatis-config.xml 中可以通过 <typeAliases>标签 定义别名
protected final TypeAliasRegistry typeAliasRegistry; protected final TypeAliasRegistry typeAliasRegistry;
@ -115,7 +115,7 @@ public class XMLConfigBuilder extends BaseBuilder {
} }
} }
``` ```
mybatis中的标签很多,所以相对应的解析方法也很多,这里挑几个比较重要的标签进行分析。 Mybatis 中的标签很多,所以相对应的解析方法也很多,这里挑几个比较重要的标签进行分析。
### 2.1 解析&lt;typeHandlers&gt;标签 ### 2.1 解析&lt;typeHandlers&gt;标签
```java ```java
private void typeHandlerElement(XNode parent) throws Exception { private void typeHandlerElement(XNode parent) throws Exception {
@ -156,7 +156,7 @@ mybatis中的标签很多所以相对应的解析方法也很多这里挑
### 2.2 解析&lt;environments&gt;标签 ### 2.2 解析&lt;environments&gt;标签
```java ```java
/** /**
* mybatis可以配置多个<environment>环境,分别用于开发、测试及生产等, * Mybatis 可以配置多个 <environment>环境,分别用于开发、测试及生产等,
* 但每个 SqlSessionFactory实例 只能选择其一 * 但每个 SqlSessionFactory实例 只能选择其一
*/ */
private void environmentsElement(XNode context) throws Exception { private void environmentsElement(XNode context) throws Exception {
@ -186,13 +186,13 @@ mybatis中的标签很多所以相对应的解析方法也很多这里挑
} }
``` ```
### 2.3 解析&lt;databaseIdProvider&gt;标签 ### 2.3 解析&lt;databaseIdProvider&gt;标签
mybatis不像hibernate那样通过hql的方式直接帮助开发人员屏蔽不同数据库产品在sql语法上的差异针对不同的数据库产品mybatis往往要编写不同的sql语句。但在mybatis-config.xml配置文件中可以通过&lt;databaseIdProvider&gt;定义所有支持的数据库产品的databaseId然后在映射配置文件中定义sql语句节点时通过databaseId指定该sql语句应用的数据库产品,也可以达到类似的屏蔽数据库产品的功能。 Mybatis 不像 Hibernate 那样,通过 HQL 的方式直接帮助开发人员屏蔽不同数据库产品在 sql语法 上的差异,针对不同的数据库产品, Mybatis 往往要编写不同的 sql语句。但在 mybatis-config.xml配置文件 中,可以通过 &lt;databaseIdProvider&gt; 定义所有支持的数据库产品的 databaseId然后在映射配置文件中定义 sql语句节点 时,通过 databaseId 指定该 sql语句 应用的数据库产品,也可以达到类似的屏蔽数据库产品的功能。
mybatis初始化时会根据前面解析到的DataSource来确认当前使用的数据库产品然后在解析映射文件时加载不带databaseId属性的sql语句 及 带有databaseId属性的sql语句其中带有databaseId属性的sql语句优先级更高会被优先选中。 Mybatis 初始化时,会根据前面解析到的 DataSource 来确认当前使用的数据库产品,然后在解析映射文件时,加载不带 databaseId属性 sql语句 及带有 databaseId属性 sql语句其中带有 databaseId属性 sql语句 优先级更高,会被优先选中。
```java ```java
/** /**
* 解析 <databaseIdProvider>节点,并创建指定的 DatabaseIdProvider对象 * 解析 <databaseIdProvider>节点,并创建指定的 DatabaseIdProvider对象
* 该对象会返回databaseId的值mybatis会根据databaseId选择对应的sql语句去执行 * 该对象会返回 databaseId的值Mybatis 会根据 databaseId 选择对应的 sql语句 去执行
*/ */
private void databaseIdProviderElement(XNode context) throws Exception { private void databaseIdProviderElement(XNode context) throws Exception {
DatabaseIdProvider databaseIdProvider = null; DatabaseIdProvider databaseIdProvider = null;
@ -217,7 +217,7 @@ mybatis初始化时会根据前面解析到的DataSource来确认当前使用
} }
} }
``` ```
mybatis提供了DatabaseIdProvider接口该接口的核心方法为getDatabaseId(DataSource dataSource)主要根据dataSource查找对应的databaseId并返回。该接口的主要实现类为VendorDatabaseIdProvider。 Mybatis 提供了 DatabaseIdProvider接口该接口的核心方法为 getDatabaseId(DataSource dataSource),主要根据 dataSource 查找对应的 databaseId 并返回。该接口的主要实现类为 VendorDatabaseIdProvider。
```java ```java
public class VendorDatabaseIdProvider implements DatabaseIdProvider { public class VendorDatabaseIdProvider implements DatabaseIdProvider {
@ -280,7 +280,7 @@ public class VendorDatabaseIdProvider implements DatabaseIdProvider {
} }
``` ```
### 2.4 解析&lt;mappers&gt;标签 ### 2.4 解析&lt;mappers&gt;标签
mybatis初始化时除了加载mybatis-config.xml文件还会加载全部的映射配置文件mybatis-config.xml文件的&lt;mapper&gt;节点会告诉mybatis去哪里查找映射配置文件,及使用了配置注解标识的接口。 Mybatis 初始化时,除了加载 mybatis-config.xml文件还会加载全部的映射配置文件mybatis-config.xml 文件的 &lt;mapper&gt;节点 会告诉 Mybatis 去哪里查找映射配置文件,及使用了配置注解标识的接口。
```java ```java
/** /**
* 解析 <mappers>节点,本方法会创建 XMLMapperBuilder对象 加载映射文件,如果映射配置文件存在 * 解析 <mappers>节点,本方法会创建 XMLMapperBuilder对象 加载映射文件,如果映射配置文件存在
@ -297,7 +297,7 @@ mybatis初始化时除了加载mybatis-config.xml文件还会加载全部
configuration.addMappers(mapperPackage); configuration.addMappers(mapperPackage);
} else { } else {
// 获取 <mapper>节点 的 resource、url、mapperClass属性这三个属性互斥只能有一个不为空 // 获取 <mapper>节点 的 resource、url、mapperClass属性这三个属性互斥只能有一个不为空
// mybatis提供了通过包名、映射文件路径、类全名、URL四种方式引入映射器。 // Mybatis 提供了通过包名、映射文件路径、类全名、URL 四种方式引入映射器。
// 映射器由一个接口和一个 XML配置文件 组成XML文件 中定义了一个 命名空间namespace // 映射器由一个接口和一个 XML配置文件 组成XML文件 中定义了一个 命名空间namespace
// 它的值就是接口对应的全路径。 // 它的值就是接口对应的全路径。
String resource = child.getStringAttribute("resource"); String resource = child.getStringAttribute("resource");
@ -376,9 +376,9 @@ XMLMapperBuilder也根据配置文件进行了一系列节点解析我们着
### 3.1 解析&lt;resultMap&gt;节点 ### 3.1 解析&lt;resultMap&gt;节点
select语句 查询得到的结果是一张二维表,水平方向上是一个个字段,垂直方向上是一条条记录。而 Java 是面向对象的程序设计语言对象是根据类的定义创建的类之间的引用关系可以认为是嵌套结构。JDBC编程 中,为了将结果集中的数据映射成 VO对象我们需要自己写代码从结果集中获取数据然后将数据封装成对应的 VO对象并设置好对象之间的关系这种 ORM 的过程中存在大量重复的代码。 select语句 查询得到的结果是一张二维表,水平方向上是一个个字段,垂直方向上是一条条记录。而 Java 是面向对象的程序设计语言对象是根据类的定义创建的类之间的引用关系可以认为是嵌套结构。JDBC编程 中,为了将结果集中的数据映射成 VO对象我们需要自己写代码从结果集中获取数据然后将数据封装成对应的 VO对象并设置好对象之间的关系这种 ORM 的过程中存在大量重复的代码。
mybatis通过&lt;resultMap&gt;节点定义了ORM规则可以满足大部分的映射需求减少重复代码提高开发效率。 Mybatis 通过 &lt;resultMap&gt;节点 定义了 ORM规则可以满足大部分的映射需求减少重复代码提高开发效率。
在分析&lt;resultMap&gt;节点的解析过程之前先看一下该过程使用的数据结构。每个ResultMapping对象记录了结果集中的一列与JavaBean中一个属性之间的映射关系。&lt;resultMap&gt;节点下除了&lt;discriminator&gt;子节点的其它子节点 都会被解析成对应的ResultMapping对象。 在分析 &lt;resultMap&gt;节点 的解析过程之前,先看一下该过程使用的数据结构。每个 ResultMapping对象 记录了结果集中的一列与 JavaBean 中一个属性之间的映射关系。&lt;resultMap&gt;节点 下除了 &lt;discriminator&gt;子节点 的其它子节点,都会被解析成对应的 ResultMapping对象。
```java ```java
public class ResultMapping { public class ResultMapping {
@ -442,7 +442,7 @@ public class ResultMap {
private Boolean autoMapping; private Boolean autoMapping;
} }
``` ```
了解了ResultMapping 和ResultMap 记录的信息之后,下面开始介绍&lt;resultMap&gt;节点的解析过程。在XMLMapperBuilder中通过resultMapElements()方法解析映射配置文件中的全部&lt;resultMap&gt;节点,该方法会循环调用resultMapElement()方法处理每个resultMap节点。 了解了 ResultMapping 和 ResultMap 记录的信息之后,下面开始介绍 &lt;resultMap&gt;节点 的解析过程。在 XMLMapperBuilder 中通过 resultMapElements()方法 解析映射配置文件中的全部 &lt;resultMap&gt;节点,该方法会循环调用 resultMapElement()方法 处理每个 &lt;resultMap&gt; 节点。
```java ```java
private ResultMap resultMapElement(XNode resultMapNode) throws Exception { private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
return resultMapElement(resultMapNode, Collections.<ResultMapping> emptyList()); return resultMapElement(resultMapNode, Collections.<ResultMapping> emptyList());
@ -498,7 +498,7 @@ public class ResultMap {
} }
} }
``` ```
从上面的代码我们可以看到,mybatis从&lt;resultMap&gt;节点获取到id属性和type属性值之后就会通过XMLMapperBuilder的buildResultMappingFromContext()方法为&lt;result&gt;节点创建对应的ResultMapping 对象。 从上面的代码我们可以看到,Mybatis 从 &lt;resultMap&gt;节点 获取到 id属性 和 type属性值 之后,就会通过 XMLMapperBuilder 的 buildResultMappingFromContext()方法 为 &lt;result&gt;节点 创建对应的 ResultMapping对象。
```java ```java
/** /**
* 根据上下文环境构建 ResultMapping * 根据上下文环境构建 ResultMapping
@ -605,12 +605,12 @@ public class MapperBuilderAssistant extends BaseBuilder {
} }
``` ```
## 4 XMLStatementBuilder ## 4 XMLStatementBuilder
这一部分看的不是很懂,暂时保留,日后深入理解了再写。
## 5 绑定Mapper接口 ## 5 绑定Mapper接口
通过之前对binding模块的解析可知每个映射配置文件的命名空间可以绑定一个Mapper接口并注册到MapperRegistry中。XMLMapperBuilder的bindMapperForNamespace()方法中完成了映射配置文件与对应Mapper 接 通过之前对 binding模块 的解析可知,每个映射配置文件的命名空间可以绑定一个 Mapper接口并注册到 MapperRegistry中。XMLMapperBuilder 的 bindMapperForNamespace()方法 中,完成了映射配置文件与对应 Mapper接口 的绑定。
口的绑定。
```java ```java
public class XMLMapperBuilder extends BaseBuilder { public class XMLMapperBuilder extends BaseBuilder {
private void bindMapperForNamespace() { private void bindMapperForNamespace() {

@ -0,0 +1,54 @@
## Linux 网络 IO 模型简介
Linux 的内核将所有外部设备都看做一个文件来操作对一个文件的读写操作会调用内核提供的系统命令返回一个fd (file descriptor文件描述符)。而对一个 socket 的读写也会有相应的描述符,称为 socket fd (socket 描述符),描述符就是一个数字,它指向内核中的一个结构体(文件路径,数据区等一些属性)。根据UNIX网络编程对 I/O模型 的分类UNIX 提供了5种 I/O模型分别如下。
#### 1、阻塞IO模型
在内核将数据准备好之前系统调用会一直等待所有的套接字Socket传来数据默认的是阻塞方式。
![avatar](/images/Netty/阻塞IO模型.png)
Java 中的 socket.read()方法 最终会调用底层操作系统的 recvfrom方法OS 会判断来自网络的数据报是否准备好当数据报准备好了之后OS 就会将数据从内核空间拷贝到用户空间(因为我们的用户程序只能获取用户空间的内存,无法直接获取内核空间的内存)。拷贝完成之后 socket.read() 就会解除阻塞,并得到网络数据的结果。
BIO中的阻塞就是阻塞在2个地方
1. OS 等待数据报通过网络发送过来,如果建立连接后数据一直没过来,就会白白浪费线程的资源;
2. 将数据从内核空间拷贝到用户空间。
在这2个时候我们的线程会一直被阻塞啥事情都不干。
#### 2、非阻塞IO模型
![avatar](/images/Netty/非阻塞IO模型.png)
每次应用程序询问内核是否有数据报准备好当有数据报准备好时就进行拷贝数据报的操作从内核拷贝到用户空间和拷贝完成返回的这段时间应用进程是阻塞的。但在没有数据报准备好时并不会阻塞程序内核直接返回未准备好的信号等待应用进程的下一次询问。但是轮寻对于CPU来说是较大的浪费一般只有在特定的场景下才使用。
从图中可以看到非阻塞IO 的 recvfrom调用 会立即得到一个返回结果(数据报是否准备好)我们可以根据返回结果继续执行不同的逻辑。而阻塞IO 的recvfrom调用如果无数据报准备好一定会被阻塞住。虽然 非阻塞IO 比 阻塞IO 少了一段阻塞的过程,但事实上 非阻塞IO 这种方式也是低效的,因为我们不得不使用轮询方法区一直问 OS“我的数据好了没啊”。
**BIO 不会在 拷贝数据之前 阻塞但会在将数据从内核空间拷贝到用户空间时阻塞。一定要注意这个地方Non-Blocking 还是会阻塞的。**
#### 3、IO复用模型
Linux 提供 select/poll进程通过将一个或多个 fd 传递给 select 或 poll系统 调用,阻塞发生在 select/poll 操作上。select/poll 可以帮我们侦测多个 fd 是否处于就绪状态,它们顺序扫描 fd 是否就绪,但支持的 fd 数量有限因此它的使用也受到了一些制约。Linux 还提供了一个 epoll系统调用epoll 使用 基于事件驱动方式 代替 顺序扫描,因此性能更高,当有 fd 就绪时,立即回调函数 rollback。
![avatar](/images/Netty/IO复用模型.png)
#### 4、信号驱动IO模型
首先开启套接口信号驱动IO功能并通过系统调用 sigaction 执行一个信号处理函数(此系统调用立即返回,进程继续工作,它是非阻塞的)。当数据准备就绪时,就为该进程生成一个 SIGIO信号通过信号回调通知应用程序调用 recvfrom 来读取数据,并通知主循环函数处理数据。
![avatar](/images/Netty/信号驱动IO模型.png)
#### 5、异步IO模型
告知内核启动某个操作,并让内核在整个操作完成后(包括将数据从内核复制到用户自己的缓冲区)通知我们。这种模型与信号驱动模型的主要区别是信号驱动IO 由内核通知我们何时可以开始一个 IO 操作异步IO模型 由内核通知我们 IO操作何时已经完成。
![avatar](/images/Netty/异步IO模型.png)
从这五种 IO模型的结构 也可以看出阻塞程度阻塞IO>非阻塞IO>多路转接IO>信号驱动IO>异步IO效率是由低到高的。
## IO 多路复用技术
Java NIO 的核心类库中 多路复用器Selector 就是基于 epoll 的多路复用技术实现。
在 IO编程 过程中,当需要同时处理多个客户端接入请求时,可以利用多线程或者 IO多路复用技术 进行处理。IO多路复用技术 通过把多个 IO 的阻塞复用到同一个 select 的阻塞上,从而使得系统在单线程的情况下可以同时处理多个客户端请求。与传统的多线程/多进程模型比IO多路复用 的最大优势是系统开销小系统不需要创建新的额外进程或线程也不需要维护这些进程和线程的运行降低了系统的维护工作量节省了系统资源IO多路复用 的主要应用场景如下。
- 服务器需要同时处理多个处于监听状态或者多个连接状态的套接字;
- 服务器需要同时处理多种网络协议的套接字。
目前支持 IO多路复用 的系统调用有 select、pselect、poll、epoll在 Linux网络编程 过程中,很长一段时间都使用 select 做轮询和网络事件通知,然而 select 的一些固有缺陷导致了它的应用受到了很大的限制,最终 Linux 选择了 epoll。epoll 与 select 的原理比较类似,为了克服 select 的缺点epoll 作了很多重大改进,现总结如下。
1. 支持一个进程打开的 socket描述符 (fd) 不受限制(仅受限于操作系统的最大文件句柄数)
2. IO效率 不会随着 FD 数目的增加而线性下降;
3. epoll的API更加简单。
值得说明的是,用来克服 select/poll 缺点的方法不只有 epoll, epoll 只是一种 Linux 的实现方案。

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Loading…
Cancel
Save