分布式事务讲解 - TX-LCN分布式事务框架(含LCN、TCC、TXC三种模式)
分布式事务讲解 - TX-LCN分布式事务框架(含LCN、TCC、TXC三种模式)
- 分布式事务系列博客:
- TX-LCN框架原理
- LCN
- 原理及主要特点
- 代码实现
- 实现场景
- 创建数据库及表(三个数据库,两个表)
- 创建工程(四个)
- 事务管理系统(lcn-tm)
- 引依赖
- 写配置
- 加注解
- Eureka注册及发现系统(eureka)
- 引依赖
- 写配置
- 加注解
- 订单系统(lcn-order)
- 引依赖
- 写配置
- 加注解
- 启动类定义RestTemplate的bean(用于调用lcn-pay系统方法)
- 添加测试Controller类(OrderController)
- 支付系统(lcn-pay)
- 引依赖(与lcn-order系统依赖相同)
- 写配置
- 加注解
- 添加测试Controller类(PayController)
- 执行测试
- 测试事务执行成功
- 测试事务执行失败
- TCC
- 原理及主要特点
- 代码实现(以LCN测试代码为基础添加TCC代码)
- 订单系统(lcn-order)
- 新增测试Controller类(OrderTccController)
- 支付系统(lcn-pay)
- 新增测试Controller类(PayTccController)
- 执行测试
- 测试事务执行成功
- 测试事务执行失败
- TXC
- 原理及主要特点
- 总结
- 源码地址
分布式事务系列博客:
【分布式事务讲解 - 2PC、3PC】
今天所讲解的TX-LCN框架,里面集成了LCN、TCC、TXC三种事务模式,并且TX-LCN是中国开发的开源框架,在源码里面可以看到有中文注释,比较有亲切感,,我们分别来详细讲一下这三种模式,并且带有代码实现。
TX-LCN框架原理
框架主要由事务参与者(TxClient)、事务管理者(TxManager)两部分构成,事务控制的原理图如下:
由图中我们就能理解TX-LCN框架的原理了,第四步可以作为阶段分割线,第四步上面所有操作就像是2PC的第一阶段,第四步及下面所有操作就像是2PC的第二阶段,如果大家忘记了2PC的话,可以看一下我写的前一篇博客【分布式事务讲解 - 2PC、3PC】,这个时序图其实就是框架源码流程的翻译,源码中也用了很多巧妙的手法来实现整体事务的工作流程,这些我会在下面介绍旗下三种模式的时候给大家一起讲解。
LCN
原理及主要特点
- LCN模式使用了代理技术,把本地事务的数据库连接维持住不释放,通过TxManager来统一控制事务。
- 在本地事务中,对事务进行的commit/rollback/close操作都是假操作,大家再远那种可以看到代理后的数据库连接中的commit/rollback/close方法都是空方法,限制本地事务进行除执行SQL以外的其他操作。
- 可以说在本地事务执行SQL之后就会一直保持数据库连接,直到TxManager发来提交或者回滚操作才会使本地事务释放数据库连接,这样增加了数据库连接的占用时长。
- 最后一阶段的TxManager发出的提交或者回滚命令是通过Netty发送的,会循环所有TxClient并发送指令。
代码实现
不光是对LCN这个模式的实现,对于所有框架的实现无非主要就三个步骤:
- 引依赖
- 写配置
- 加注解
像我之前写的【Spring Cloud框架搭建系列】大家也能看得出来,下面我给大家展示一下。
实现场景
用户买东西下单,下完单之后保存【单据记录】以及【支付金额】,只要有一个操作执行失败,那就下单失败,否则,整体流程执行成功,下单成功。
创建数据库及表(三个数据库,两个表)
-- 用户维护日志及异常信息,可按需创建表
CREATE DATABASE tx-manager;CREATE DATABASE lcn-pay;CREATE TABLE `tbl_pay` (`id` int(11) NOT NULL AUTO_INCREMENT,`pay_name` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;CREATE DATABASE lcn-order;CREATE TABLE `tbl_order` (`id` int(11) NOT NULL AUTO_INCREMENT,`order_name` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
创建工程(四个)
事务管理系统(lcn-tm)
引依赖
<!-- txManager所需依赖 -->
<dependency><groupId>com.codingapi.txlcn</groupId><artifactId>txlcn-tm</artifactId><version>5.0.2.RELEASE</version>
</dependency>
<dependency><groupId>com.codingapi.txlcn</groupId><artifactId>txlcn-tc</artifactId><version>5.0.2.RELEASE</version>
</dependency>
<!-- 用于txManager通知txClient -->
<dependency><groupId>com.codingapi.txlcn</groupId><artifactId>txlcn-txmsg-netty</artifactId><version>5.0.2.RELEASE</version>
</dependency>
<!--tm-->
写配置
# TM事务管理器的服务端WEB访问端口。提供一个可视化的界面。端口自定义。
server.port=3000# TM事务管理器,需要访问数据库,实现分布式事务状态记录。
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/tx-manager?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=wk3515134# 为spring应用起名。
spring.application.name=tx-lcn-transaction-manager# TM事务管理器,提供的WEB管理平台的登录密码。无用户名。 默认是codingapi
tx-lcn.manager.admin-key=wk3515134
# 日志。如果需要TM记录日志。则开启,赋值为true,会自动为tx-manager数据库添加t_logger表。
tx-lcn.logger.enabled=true
加注解
在lcn-tm工程启动类添加注解
@EnableTransactionManagerServer
Eureka注册及发现系统(eureka)
引依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
写配置
spring:application:name: cloud-eureka
eureka:instance:# 在hosts文件中修改内容hostname: euk-server-one.comclient:register-with-eureka: falsefetch-registry: falseservice-url:# 5 24defaultZone: http://euk-server-one.com:8080/eureka/
加注解
在eureka工程启动类添加注解
@EnableEurekaServer
如果配置后eureka启动失败,那就移步我的一篇博客【Spring Cloud完整组件搭建之路-Eureka】。
订单系统(lcn-order)
引依赖
<!-- lcn -->
<dependency><groupId>com.codingapi.txlcn</groupId><artifactId>txlcn-tc</artifactId><version>5.0.2.RELEASE</version>
</dependency><dependency><groupId>com.codingapi.txlcn</groupId><artifactId>txlcn-txmsg-netty</artifactId><version>5.0.2.RELEASE</version>
</dependency><!-- json-lib -->
<dependency><groupId>net.sf.json-lib</groupId><artifactId>json-lib</artifactId><version>2.4</version><classifier>jdk15</classifier>
</dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId><version>2.2.2.RELEASE</version>
</dependency><!-- mysql:MyBatis相关依赖 -->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.0.0</version>
</dependency><!-- mysql:mysql驱动 -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency><!-- mysql:阿里巴巴数据库连接池 -->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.12</version>
</dependency>
写配置
server:port: 1000
# 服务名称
spring:application:name: lcn-orderdatasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/lcn-order?characterEncoding=UTF-8&serverTimezone=Asia/Shanghaiusername: rootpassword: wk3515134dbcp2:initial-size: 5min-idle: 5max-total: 5max-wait-millis: 200validation-query: SELECT 1test-while-idle: truetest-on-borrow: falsetest-on-return: falsemybatis:mapper-locations:- classpath:mapper/*.xmleureka:client:service-url:# 注册进eurekadefaultZone: http://euk-server-one.com:8080/eureka/# 事务处理器地址,需要先启动lcn-tm系统并登录,端口就在下图所示位置
tx-lcn:client:manager-address: 127.0.0.1:3100
加注解
在lcn-order工程启动类添加注解
@EnableDistributedTransaction
启动类定义RestTemplate的bean(用于调用lcn-pay系统方法)
@Bean
@LoadBalanced
public RestTemplate restTemplate(){return new RestTemplate();
}
添加测试Controller类(OrderController)
import com.codingapi.txlcn.tc.annotation.LcnTransaction;
import com.hepai.lcnorder.dao.TblOrderDao;
import com.hepai.lcnorder.entity.TblOrder;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;@RestController
public class OrderController {@Autowiredprivate TblOrderDao tblOrderDao;@Autowiredprivate RestTemplate restTemplate;@PostMapping("/add-order")@Transactional(rollbackFor = Exception.class) //一定要加,LCN源码会判断这个注解@LcnTransaction // 注解必须加,使用LCN模式,加在有调用分布式系统的方法之上即可public String add(@RequestBody TblOrder bean){JSONObject date = new JSONObject();date.put("payName",bean.getOrderName()+"pay");// 调用支付系统指定方法restTemplate.postForEntity("http://lcn-pay/add-pay",date,String.class);
// int i = 1/0;// 插入sql,Service、Mapper以及Entity大家自己写吧,按照普通业务逻辑写就好tblOrderDao.insert(bean);return "新增订单成功";}
}
支付系统(lcn-pay)
引依赖(与lcn-order系统依赖相同)
<!-- lcn -->
<dependency><groupId>com.codingapi.txlcn</groupId><artifactId>txlcn-tc</artifactId><version>5.0.2.RELEASE</version>
</dependency>
<dependency><groupId>com.codingapi.txlcn</groupId><artifactId>txlcn-txmsg-netty</artifactId><version>5.0.2.RELEASE</version>
</dependency>
<!-- json-lib -->
<dependency><groupId>net.sf.json-lib</groupId><artifactId>json-lib</artifactId><version>2.4</version><classifier>jdk15</classifier>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId><version>2.2.2.RELEASE</version>
</dependency><!-- mysql:MyBatis相关依赖 -->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.0.0</version>
</dependency><!-- mysql:mysql驱动 -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency><!-- mysql:阿里巴巴数据库连接池 -->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.12</version>
</dependency>
写配置
server:port: 2000
# 服务名
spring:application:name: lcn-paydatasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/lcn-pay?characterEncoding=UTF-8&serverTimezone=Asia/Shanghaiusername: rootpassword: wk3515134dbcp2:initial-size: 5min-idle: 5max-total: 5max-wait-millis: 200validation-query: SELECT 1test-while-idle: truetest-on-borrow: falsetest-on-return: false
mybatis:mapper-locations:- classpath:mapper/*.xml
eureka:client:service-url:defaultZone: http://euk-server-one.com:8080/eureka/
# 事务处理器地址,需要先启动lcn-tm系统并登录,端口就在下图所示位置
tx-lcn:client:manager-address: 127.0.0.1:3100
加注解
在lcn-pay工程启动类添加注解
@EnableDistributedTransaction
添加测试Controller类(PayController)
import com.codingapi.txlcn.tc.annotation.LcnTransaction;
import com.hepai.lcnpay.dao.TblPayDao;
import com.hepai.lcnpay.entity.TblPay;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;@RestController
public class PayController {@Autowiredprivate TblPayDao tblPayDao;@PostMapping("/add-pay")@Transactional(rollbackFor = Exception.class)@LcnTransactionpublic String addPay(@RequestBody TblPay bean){tblPayDao.insert(bean);return "新增支付成功";}
}
执行测试
测试事务执行成功
- 使用Postman调用【http://localhost:1000/add-order】,填写相应参数,执行结果:
- 查看数据库数据:
测试事务执行失败
- 修改OderController类方法,添加异常
public String add(@RequestBody TblOrder bean){JSONObject date = new JSONObject();date.put("payName",bean.getOrderName()+"pay");restTemplate.postForEntity("http://lcn-pay/add-pay",date,String.class);
// 添加异常int i = 1/0;tblOrderDao.insert(bean);return "新增订单成功";
}
使用Postman调用【http://localhost:1000/add-order】,填写相应参数,执行结果:
查看数据库数据:
可见,数据库数据没有变化,还是刚才事务执行成功插入的数据,所以,分布式事务生效。
TCC
原理及主要特点
- TCC模式在实现的时候,每个事务参与系统的业务类都需要写三个方法(业务执行方法、提交方法、取消方法),即,在【执行业务】的时候,如果分布式事务整体失败,就执行【取消方法】,如果分布式事务整体成功,就执行【提交方法】,加大了代码实现的复杂度。
- TCC模式适用于无本地事务控制的情况,比如:MongoDB、Redis等不支持事务的中间件,就可以使用TCC模式来实现分布式事务。
- TCC模式主要也是分为两个阶段,第一个阶段,各事务参与者本地直接执行SQL并提交,并反馈执行结果给事务管理者;第二阶段,事务管理者根据第一阶段的反馈判断所有事务参与者是执行【取消方法】还是执行【提交方法】。
- 如果第二阶段执行【取消方法】,需要执行第一阶段SQL的逆SQL,也就是像Mysql的Undo日志相似,第一阶段执行的insert语句需要delete来恢复,update语句需要逆update来恢复,更加大了业务的复杂度和代码的实现难度。
注意:
TCC模式第一阶段执行完SQL是直接提交的,逆SQL的准备是非常有必要的。
代码实现(以LCN测试代码为基础添加TCC代码)
订单系统(lcn-order)
新增测试Controller类(OrderTccController)
import com.codingapi.txlcn.tc.annotation.TccTransaction;
import com.hepai.lcnorder.dao.TblOrderDao;
import com.hepai.lcnorder.entity.TblOrder;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;import java.util.HashMap;
import java.util.Map;@RestController
public class OrderTccController {@Autowiredprivate TblOrderDao tblOrderDao;@Autowiredprivate RestTemplate restTemplate;@PostMapping("/add-order-tcc")@Transactional(rollbackFor = Exception.class)@TccTransaction // 使用框架的TCC模式实现分布式事务public String add(@RequestBody TblOrder bean){JSONObject date = new JSONObject();date.put("payName",bean.getOrderName()+"pay");restTemplate.postForEntity("http://lcn-pay/add-pay-tcc",date,String.class);tblOrderDao.insert(bean);Integer id = bean.getId();maps.put("a",id);
// int i = 1/0;return "新增订单成功1";}public String confirmAdd(TblOrder bean){System.out.println("order confirm ");return "新增订单成功2";}private static Map<String,Integer> maps = new HashMap<>();public String cancelAdd(TblOrder bean){Integer a = maps.get("a");System.out.println("a:"+a);// 取消方法执行sql逆操作tblOrderDao.deleteByPrimaryKey(a);System.out.println("order cancel ");return "新增订单失败";}
}
注意:
cancelAdd()和confirmAdd()不用加注解,因为框架源码中表明如果没有发现【提交方法】和【取消方法】的注解,那就默认把加了【@TccTransaction】注解的方法名第一个字母大写并加前缀【cancel】和【confirm】来分别作为【提交方法】和【取消方法】的方法名。
支付系统(lcn-pay)
新增测试Controller类(PayTccController)
import com.codingapi.txlcn.tc.annotation.TccTransaction;
import com.hepai.lcnpay.dao.TblPayDao;
import com.hepai.lcnpay.entity.TblPay;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;@RestController
public class PayTccController {@Autowiredprivate TblPayDao tblPayDao;@PostMapping("/add-pay-tcc")@Transactional(rollbackFor = Exception.class)@TccTransactionpublic String addPay(@RequestBody TblPay bean){tblPayDao.insert(bean);Integer id = bean.getId();maps.put("a",id);int i = 1/0;return "新增支付成功1";}public String confirmAddPay(TblPay bean){System.out.println("pay confirm");return "新增支付成功2";}private static Map<String,Integer> maps = new HashMap<>();/*** 逆sql* @param bean* @return*/public String cancelAddPay(TblPay bean){Integer a = maps.get("a");System.out.println("a:"+a);System.out.println("pay cancel");tblPayDao.deleteByPrimaryKey(a);return "支付失败";}
}
执行测试
测试事务执行成功
使用Postman调用【http://localhost:1000/add-order-tcc】,填写相应参数,执行结果:
查看数据库数据:
测试事务执行失败
- 修改OderTccController类方法,添加异常
public String add(@RequestBody TblOrder bean){JSONObject date = new JSONObject();date.put("payName",bean.getOrderName()+"pay");restTemplate.postForEntity("http://lcn-pay/add-pay-tcc",date,String.class);tblOrderDao.insert(bean);Integer id = bean.getId();maps.put("a",id);// 添加异常int i = 1/0;return "新增订单成功1";
}
使用Postman调用【http://localhost:1000/add-order-tcc】,填写相应参数,执行结果:
查看数据库数据:
可见,数据库数据没有变化,还是刚才事务执行成功插入的数据,所以,分布式事务生效。
TXC
原理及主要特点
- TXC模式的工作流程中执行SQL前要先查询影响的数据,如果数据量大的情况下,效率会极低。
- TXC模式只能支持SQL方式的事务模块,因为第一条,它必须要先查询影响数据,所以有要执行的SQL是大前提。
- 不会占用数据库的连接资源。
- 我个人认为,对于第一条,如果是有大量的数据,并且涉及到的影响数据表有成百上千个,那这种方式是最不建议的,这种模式对查询的效率要求极高。
总结
在TX-LCN框架中,在参与者本地支持事务的分布式事务情况下,建议使用LCN模式,参与者本地有不支持事务的情况下,只能用TCC。
源码地址
为防大家使用的时候还是出现其他问题,我把源码共享出来,希望能帮助大家。
https://gitee.com/hepai123/lcn-demo.git
分布式事务讲解 - TX-LCN分布式事务框架(含LCN、TCC、TXC三种模式)相关推荐
- Hadoop的三种模式(单机模式,伪分布式,完全分布式)以及集群的搭建
基本概念: 1. Hadoop是一个分布式文件系统的基础架构,用户可以利用集群进行高速运算和存储 2. Hadoop实现了一个分布式文件系统(Hadoop Distributed File Syste ...
- 深入学习SAP UI5框架代码系列之七:控件数据绑定的三种模式 - One Way, Two Way和OneTime实现原理比较
这是Jerry 2021年的第 8 篇文章,也是汪子熙公众号总共第 279 篇原创文章. 系列目录 (0) SAP UI5应用开发人员了解UI5框架代码的意义 (1) SAP UI5 module懒加 ...
- atitit 方便搜索的文档文章结构框架.docx 目录 1.1. 三种搜索模式 tree hash关键词模式 关联搜索,对应的三种索引 1 1.2. 好的标题规范与副标题 1 1.3. Tr
atitit 方便搜索的文档文章结构框架.docx 目录 1.1. 三种搜索模式 tree hash关键词模式 关联搜索,对应的三种索引 1 1.2. 好的标题规范与副标题 1 1.3. Tre ...
- .NET EF框架的安装、及三种开发模式
一.EF框架的安装: 要在VS(如Visual Studio 2012)中使用EF框架,就需要先进行安装. 我们需要给这个应用安装EntityFramework包,引入EF框架相关的内容,我们需要引入 ...
- php事务讲解,PHP面向对象之事务脚本模式(详解)
/* 事务脚本模式: 类似于thinkphp中的model层,或者说就是操作数据库的类. 个人觉得实践中使用起来还是挺简单方便的,就是SQL语句写死了的话,灵活性就不够. 示例代码如下: */ nam ...
- aop框架 php,xaop: 支持三种模式的AOP框架,弥补PHPer的不足,并且自带了文档的解析类库,可以一并使用,性能极好,欢迎 STAR 与 FORK。...
Xaop PHP高性能的AOP扩展 功能特色 基于对象的文档注解AOP模式 方法注入AOP模式 属性AOP模式 系统指令及其含义 xaop.method_prefix AOP文档注解需要排除的方法前缀 ...
- 自动化框架——PO设计模式自学——第三种定位方法
import time from selenium import webdriverdriver = webdriver.Firefox() driver.get('http://www.baidu. ...
- 分布式事务(二)LCN分布式事务框架
1. 简介 LCN框架在2017年6月发布第一个版本,目前最新已经达到5.0版本. LCN早期设计时,1.0版本和2.0版本设计步骤如下: 锁定事务单元(Lock) 确认事务模块状态(Confirm) ...
- 什么是事务? 事务的隔离级别和事务运行的模式分别是什么?spring 事务和分布式事务实现方式有哪些?
目录 什么是事务? 事务的隔离级别: 事务运行的模式: spring 事务实现方式: 分布式事务实现方式: 什么是事务? 百度百科中解释:指作为单个逻辑工作单元执行的一系列操作,此操作是对数据库的操作 ...
最新文章
- ThreadGroup
- mysql 连接url中useUnicode=truecharacterEncoding=UTF-8 的作用
- 八 python 环境安装
- [easyui] 在iframe中操作父窗口的组件
- Kafka简介、安装
- 中国物联网产业RFID发展机遇分析
- 首字母大写转换 java,Java InitialsTransformation(字符串首字母大小写转换)
- 还在用 Swagger(丝袜哥)生成接口文档?我推荐你试试它。。。
- 深入浅出 | 谈谈MNN GPU性能优化策略
- Python 手把手实现远程控制桌面
- 保持稳定迭代的秘密:基于Spinnaker的全自动渐进式交付
- pivot 与 unpivot函数
- 新唐N76E003与ST公司STM8S003F3芯片对比 史上最全的没有之一
- windows,远程开机,远程唤醒(WOL,Wake-on-LAN)
- 学 Python 的乐园,坚持一年,值了!
- 所见所得的OFFICE功能区编辑器(自定义界面编辑)RibbonCreator
- java jdk 1.8中lambda表达式常用方法
- 使用cpolar建立固定的SSH隧道
- 学习周记1:2019.2.18-2019.2.24
- 零基础从微软官网制作纯净的U盘启动盘
热门文章
- Entity Framework Core Migrations
- CGAL-Triangulation中的单元和面的关系
- VMware Convertor V2V转换CentOS kernel panic
- PyQt5_pyqtgraph单Y轴和双Y轴折线图
- 管理系统中计算机应用试题及答案,自考管理系统中计算机应用试题及答案
- 一开机就提示脱机工作_电脑一开机就会跳出“脱机工作”怎么解决
- 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #12 使用Memory Cgroup限制内存使用量...
- 使用angular实现60秒倒计时(手机号注册)
- iPhone手机打开这个功能,开会不用愁
- PCAN-View点了Reset后无法接收报文