分布式事务解决方案之 Alibaba Seata1.3.0

一 Seata 是什么?

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

详情查看官方文档https://seata.io/zh-cn/docs/overview/what-is-seata.html

术语

TC (Transaction Coordinator) - 事务协调者

维护全局和分支事务的状态,驱动全局事务提交或回滚。

TM (Transaction Manager) - 事务管理器

定义全局事务的范围:开始全局事务、提交或回滚全局事务。

RM (Resource Manager) - 资源管理器

管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

二 docker安装seata-server

docker

https://seata.io/zh-cn/docs/ops/deploy-by-docker.html

下载zip到本地

https://seata.io/zh-cn/blog/download.html

# 拉取镜像
docker pull seataio/seata-server
# 启动一个seata-server的容器
docker run -id --name seata-fs -p 8091:8091 --network host seataio/seata-server:latest

2.1 seata需要的数据表

取名为seata库,执行下面的表

DROP TABLE IF EXISTS `branch_table`;
CREATE TABLE `branch_table`  (`branch_id` bigint(20) NOT NULL,`xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`transaction_id` bigint(20) NULL DEFAULT NULL,`resource_group_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`branch_type` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`status` tinyint(4) NULL DEFAULT NULL,`client_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`gmt_create` datetime(6) NULL DEFAULT NULL,`gmt_modified` datetime(6) NULL DEFAULT NULL,PRIMARY KEY (`branch_id`) USING BTREE,INDEX `idx_xid`(`xid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of branch_table
-- ------------------------------ ----------------------------
-- Table structure for global_table
-- ----------------------------
DROP TABLE IF EXISTS `global_table`;
CREATE TABLE `global_table`  (`xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`transaction_id` bigint(20) NULL DEFAULT NULL,`status` tinyint(4) NOT NULL,`application_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`transaction_service_group` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`transaction_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`timeout` int(11) NULL DEFAULT NULL,`begin_time` bigint(20) NULL DEFAULT NULL,`application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`gmt_create` datetime(0) NULL DEFAULT NULL,`gmt_modified` datetime(0) NULL DEFAULT NULL,PRIMARY KEY (`xid`) USING BTREE,INDEX `idx_gmt_modified_status`(`gmt_modified`, `status`) USING BTREE,INDEX `idx_transaction_id`(`transaction_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of global_table
-- ------------------------------ ----------------------------
-- Table structure for lock_table
-- ----------------------------
DROP TABLE IF EXISTS `lock_table`;
CREATE TABLE `lock_table`  (`row_key` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`xid` varchar(96) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`transaction_id` bigint(20) NULL DEFAULT NULL,`branch_id` bigint(20) NOT NULL,`resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`table_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`pk` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`gmt_create` datetime(0) NULL DEFAULT NULL,`gmt_modified` datetime(0) NULL DEFAULT NULL,PRIMARY KEY (`row_key`) USING BTREE,INDEX `idx_branch_id`(`branch_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of lock_table
-- ------------------------------ ----------------------------
-- Table structure for undo_log
-- ----------------------------
DROP TABLE IF EXISTS `undo_log`;
CREATE TABLE `undo_log`  (`id` bigint(20) NOT NULL AUTO_INCREMENT,`branch_id` bigint(20) NOT NULL,`xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int(11) NOT NULL,`log_created` datetime(0) NOT NULL,`log_modified` datetime(0) NOT NULL,`ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE,UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

2.2 seata-server配置文件修改

# 进入seata-server容器docker exec -it seata-server /bin/bash# cd到cd resources/# 修改file.conf配置文件
vim file.conf# 提示:bash: vim: command not found
# 解决方案 这是因为vim没有安装。使用如下命令安装:
apt-get update
apt-get install vim

2.2.1 修改file.conf文件

记住手动添加service模块

## transaction log store, only used in seata-server#这里手动加入service模块
service {#transaction service group mapping#修改,可不改,fs_group随便起名字。vgroup_mapping.fs_group = "default"#only support when registry.type=file, please don't set multiple addresses# 此服务的地址default.grouplist = "47.112.174.148:8091"#disable seatadisableGlobalTransaction = false
}store {## store mode: file、db、redis#这里修改为dbmode = "db"## file store propertyfile {## store location dirdir = "sessionStore"# branch session size , if exceeded first try compress lockkey, still exceeded throws exceptionsmaxBranchSessionSize = 16384# globe session size , if exceeded throws exceptionsmaxGlobalSessionSize = 512# file buffer size , if exceeded allocate new bufferfileWriteBufferCacheSize = 16384# when recover batch read sizesessionReloadReadSize = 100# async, syncflushDiskMode = async}## database store propertydb {## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.datasource = "druid"## mysql/oracle/postgresql/h2/oceanbase etc.dbType = "mysql"#如果你是mysql8.0以上,则修改这里的驱动,不是则不修改driverClassName = "com.mysql.jdbc.Driver"url = "jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai"user = "账户"password = "密码"minConn = 5maxConn = 30globalTable = "global_table"branchTable = "branch_table"lockTable = "lock_table"queryLimit = 100maxWait = 5000}## redis store propertyredis {host = "127.0.0.1"port = "6379"password = ""database = "1"minConn = 1maxConn = 10queryLimit = 100}}

2.2.2 修改registry.conf文件

registry {# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa# 修改这里为nacostype = "nacos"nacos {application = "seata-server"serverAddr = "47.112.174.148:8848"group = "SEATA_GROUP"namespace = ""cluster = "default"username = "nacos"password = "nacos"}eureka {serviceUrl = "http://localhost:8761/eureka"application = "default"weight = "1"}redis {serverAddr = "localhost:6379"db = 0password = ""cluster = "default"timeout = 0}zk {cluster = "default"serverAddr = "127.0.0.1:2181"sessionTimeout = 6000connectTimeout = 2000username = ""password = ""}consul {cluster = "default"serverAddr = "127.0.0.1:8500"}etcd3 {cluster = "default"serverAddr = "http://localhost:2379"}sofa {serverAddr = "127.0.0.1:9603"application = "default"region = "DEFAULT_ZONE"datacenter = "DefaultDataCenter"cluster = "default"group = "SEATA_GROUP"addressWaitTime = "3000"}file {name = "file.conf"}
}config {# file、nacos 、apollo、zk、consul、etcd3#修改这里为nacostype = "nacos"nacos {serverAddr = "47.112.174.148:8848"namespace = ""group = "SEATA_GROUP"username = "nacos"password = "nacos"}consul {serverAddr = "127.0.0.1:8500"}apollo {appId = "seata-server"apolloMeta = "http://192.168.1.204:8801"namespace = "application"}zk {serverAddr = "127.0.0.1:2181"sessionTimeout = 6000connectTimeout = 2000username = ""password = ""}etcd3 {serverAddr = "http://localhost:2379"}file {name = "file.conf"}
}

2.2.3 最后退出重启seata-server容器

在nacos中查看seata-server是否注册上

三 seata配置文件注册到注册中心

3.1 到seata地址下载源码https://github.com/seata/seata

3.2 修改配置文件

下载解压后进入seata-1.3.0\seata-1.3.0\script\config-center目录下修改config.txt文件

transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableClientBatchSendRequest=false
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3
# 配置seata-server的信息 fs_group就是之前在file.conf中配置的
service.vgroupMapping.fs_group=default
# 配置seata-server的ip
service.default.grouplist=47.112.174.148:8091service.enableDegrade=false
service.disableGlobalTransaction=false
client.rm.asyncCommitBufferLimit=10000
client.rm.lock.retryInterval=10
client.rm.lock.retryTimes=30
client.rm.lock.retryPolicyBranchRollbackOnConflict=true
client.rm.reportRetryCount=5
client.rm.tableMetaCheckEnable=false
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
# 这里改为db
store.mode=db
store.file.dir=file_store/data
store.file.maxBranchSessionSize=16384
store.file.maxGlobalSessionSize=512
store.file.fileWriteBufferCacheSize=16384
store.file.flushDiskMode=async
store.file.sessionReloadReadSize=100
store.db.datasource=druid
store.db.dbType=mysql
# mysql 版本5的就是这个驱动,8要改
store.db.driverClassName=com.mysql.jdbc.Driver
# mysql地址
store.db.url=jdbc:mysql://47.112.174.148:3306/seata?useUnicode=true
# mysql用户名密码
store.db.user=root
store.db.password=root
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
store.redis.host=127.0.0.1
store.redis.port=6379
store.redis.maxConn=10
store.redis.minConn=1
store.redis.database=0
store.redis.password=null
store.redis.queryLimit=100
server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
client.undo.dataValidation=true
client.undo.logSerialization=jackson
client.undo.onlyCareUpdateColumns=true
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
client.undo.logTable=undo_log
client.log.exceptionRate=100
transport.serialization=seata
transport.compressor=none
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898

3.3 执行nacos-config.sh脚本

然后执行源码seata\seata-1.3.0\script\config-center\nacos\nacos-config.sh 这个脚本
注意需要编辑nacos-config.sh 脚本中得nacos地址

可以使用git命令窗口执行,看到如下图片则代表执行成功

3.4 去nacos查看配置文件是否推送成功

nacos配置管理,配置列表中可以看到如下图所示配置,即代表成功

四 快速开始SEATA AT 模式

4.1 环境搭建docker

nacos mysql seata-server

4.1.1 本地启动seata-server

去nacos查看seata-server是注册成功

让我们从一个微服务示例开始

4.2 数据准备

用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持:

  • 仓储服务:对给定的商品扣除仓储数量。
  • 订单服务:根据采购需求创建订单。
  • 帐户服务:从用户帐户中扣除余额。

4.3 为示例业务创建表

创建库存表,订单表,账户表

# 库存表
DROP TABLE IF EXISTS `storage_tb`;
CREATE TABLE `storage_tb` (`id` int(11) NOT NULL AUTO_INCREMENT,`commodity_code` varchar(255) DEFAULT NULL,`count` int(11) DEFAULT 0,PRIMARY KEY (`id`),UNIQUE KEY (`commodity_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;# 订单表
DROP TABLE IF EXISTS `order_tb`;
CREATE TABLE `order_tb` (`id` int(11) NOT NULL AUTO_INCREMENT,`user_id` varchar(255) DEFAULT NULL,`commodity_code` varchar(255) DEFAULT NULL,`count` int(11) DEFAULT 0,`money` int(11) DEFAULT 0,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;# 账户表
DROP TABLE IF EXISTS `account_tb`;
CREATE TABLE `account_tb` (`id` int(11) NOT NULL AUTO_INCREMENT,`user_id` varchar(255) DEFAULT NULL,`money` int(11) DEFAULT 0,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

4.4 创建 UNDO_LOG 表(每个库下都要创建)

创建 UNDO_LOG 表
SEATA AT 模式需要 UNDO_LOG 表-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log
CREATE TABLE `undo_log` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`branch_id` bigint(20) NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int(11) NOT NULL,`log_created` datetime NOT NULL,`log_modified` datetime NOT NULL,`ext` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

最终表结构如图

4.4.1 为每个表初始化默认数据

4.5 搭建springcloud项目

4.5.1 源代码

大致就是order服务生成订单使用feign远程调用去减库存,扣钱,和生成订单
在生成订单的未付的方法上使用@GlobalTransactional就实现了全局事务控制
若在扣完钱后制造除0异常,就会导致订单生成不成功,但是扣了钱和减了库存,所以需要使用分布式事务控制

4.5.2 seata pom依耐

<dependencies><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-seata</artifactId><version>2.2.0.RELEASE</version><exclusions><exclusion><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId></exclusion><exclusion><groupId>io.seata</groupId><artifactId>seata-all</artifactId></exclusion></exclusions></dependency><dependency><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId><version>1.3.0</version></dependency><dependency><groupId>io.seata</groupId><artifactId>seata-all</artifactId><version>1.3.0</version></dependency></dependencies>

4.5.3 application.yml(以order-service为例子)

server:port: 8001
spring:application:name: seata-orderdatasource:username: 账户password: 密码url: jdbc:mysql://47.112.174.148:3306/orderbasedriver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSource #自定义数据源cloud:nacos:discovery:server-addr: 47.112.174.148:8848
#seata配置
seata:enabled: true# 起个当前服务的seata名字application-id: seata-order#这里的名字与file.conf中vgroup_mapping.my_test_tx_group = "default"相同tx-service-group: fs_groupenable-auto-data-source-proxy: true#  use-jdk-proxy: falseservice:#这里的名字与file.conf中vgroup_mapping.my_test_tx_group = "default"相同vgroup-mapping:my_test_tx_group: default#这里的名字与file.conf中default.grouplist = "127.0.0.1:8091"相同grouplist:default: 127.0.0.1:8091#      disable-global-transaction: falseconfig:type: nacosnacos:namespace:#这里的地址就是你的nacos的地址,可以更换为线上serverAddr: 47.112.174.148:8848#这里的名字就是registry.conf中 nacos的group名字group: SEATA_GROUPuserName: "nacos"password: "nacos"registry:type: nacosnacos:application: seata-server#这里的地址就是你的nacos的地址,可以更换为线上server-addr: 47.112.174.148:8848#这里的名字就是registry.conf中 nacos的group名字group: SEATA_GROUPnamespace:userName: "nacos"password: "nacos"# feign配置
feign:hystrix:enabled: trueclient:config:default:   #配置全局的feign的调用超时时间  如果 有指定的服务配置 默认的配置不会生效connectTimeout: 60000 # 指定的是 消费者 连接服务提供者的连接超时时间 是否能连接  单位是毫秒readTimeout: 60000  # 指定的是调用服务提供者的 服务 的超时时间()  单位是毫秒
#hystrix 配置
hystrix:command:default:execution:timeout:#如果enabled设置为false,则请求超时交给ribbon控制enabled: trueisolation:strategy: SEMAPHOREthread:# 熔断器超时时间,默认:1000/毫秒timeoutInMilliseconds: 20000# 配置日志
#mybatis-plus:
#  configuration:
#    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

4.5.4 启动微服务测试

在生成订单的未付的方法上使用@GlobalTransactional就实现了全局事务控制
在order的生成订单的业务中制作异常,查看事务是否被控制

分布式事务解决方案之 Alibaba Seata1.3.0 seata-server 1.3.0相关推荐

  1. 分布式事务解决方案 - SpringCloud Alibaba Seata

    目录 github代码:GitHub - 18409489244/seata: 基于springcloud alibaba seata 的分布式事务demo 一.常见分布式事务解决方案 二.分布式事务 ...

  2. Seata聚合 AT、TCC、SAGA 、 XA事务模式打造一站式的分布式事务解决方案

    Seata Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务.在 Seata 开源之前,Seata 对应的内部版本在阿里经济体内部一直扮演着分布式一 ...

  3. EasyTransaction 1.3.0 发布,一站式分布式事务解决方案

    EasyTransaction 是一个一站式分布式事务解决方案,其包括了 TCC 事务,自动补偿(seata/fescar AT 功能),补偿事务,可靠消息,SAGA 等等多种事务模式,并能混合各种事 ...

  4. SpringCloud Alibaba 微服务架构(十一)- 分布式事务解决方案及理论基础篇

    前言 在传统的单体应用架构中,例如经典的SSM,项目会采用分层架构模式:数据库访问层.业务逻辑层.控制层,从前端到后台所有的代码都是一个或者几个开发者去完成,该架构模式没有对我们业务逻辑代码实现拆分. ...

  5. 分布式事务解决方案,Seata的基本配置和使用

    文章目录 1. 分布式事务介绍 ①:本地事务 ②:分布式事务 ③:常见的分布式事务解决方案 3. 2PC与3PC ①:2PC与3PC的区别 3. Seata介绍 ①:Seata的三种角色 ②:Seat ...

  6. 阿里开源分布式事务解决方案 Fescar 全解析

    广为人知的阿里分布式事务解决方案:GTS(Global Transaction Service),已正式推出开源版本,取名为"Fescar",希望帮助业界解决微服务架构下的分布式事 ...

  7. 阿里开源分布式事务解决方案 Fescar

    微服务倡导将复杂的单体应用拆分为若干个功能简单.松耦合的服务,这样可以降低开发难度.增强扩展性.便于敏捷开发.当前被越来越多的开发者推崇,系统微服务化后,一个看似简单的功能,内部可能需要调用多个服务并 ...

  8. 基于activemq的分布式事务解决方案

    1.分布式事务出现场景 场景描述:支付宝转账余额宝 分布式事务必须满足的条件: 1.远程RPC调用,支付宝和余额宝存在接口调用 2.支付宝和余额宝使用不同的数据库 如图: 2.分布式事务解决方案 1. ...

  9. 微服务分布式事务解决方案Seata

    文章目录 一.Seata是什么? 二.使用步骤 1.引入库 2.读入数据 总结 一.什么是Seata? Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用 的分布式事务服务.Sea ...

最新文章

  1. 哈夫曼树的生成及哈夫曼编码
  2. 颤抖吧,打工人!深信服推出员工离职倾向、工作摸鱼监测系统!
  3. 干货 | 使用FFT变换自动去除图像中严重的网纹
  4. python数据分析常用的算法_萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第二节 线性回归算法 (上)理解篇...
  5. android textview 设置字体,Android TextView设置字体风格
  6. 第二天2017/03/29: 字符串操作
  7. linux time dev null,/dev/null丟失后
  8. close_wait过多服务器无响应,记一次大量CLOSE_WAIT连接导致的服务宕机
  9. Effective C++ 读后感
  10. HDU4417 线段树 + 离线处理
  11. 1、Python基本对象类型----数字
  12. SHELL基础命令大全
  13. “油猴”的五大神级脚本
  14. Xamarin学习笔记之尝试篇
  15. Linux 题库及答案永久开放共同学习进步
  16. 再见SNDA,在离职之后
  17. APM-Skywalking调研及实施报告
  18. processing软件使用python_Python processing学习
  19. 计算机考试显示延时一分钟,电脑显示延迟解决方法教程
  20. java读取rar中的excel文件_java 通用文件下载 excel,pdf,word,jpg,exe,rar

热门文章

  1. 水晶报表-横向设计页面,设置网格高度
  2. JavaScript调用服务器事件
  3. Android 在WebView中获取网页源码
  4. step5 . day2 网络编程 基于TPC协议的网络编程流程及API
  5. AngularJs ng-route路由详解
  6. 【Centos】修改系统字符集
  7. 一个毕业生对大学爱情和奋斗的思考!
  8. 如何诊断crs 安装时 root.sh 脚本执行错误
  9. ansible-playbook 手工编译安装nginx
  10. ansible的系统模块8