TCC细读 - 1 例子流程
http://www.iocoder.cn/categories/TCC-Transaction/
https://github.com/changmingxie/tcc-transaction
细读tcc,理解事物实现的本质
顾名思义,TCC - Try(完成所有业务检查,预留必须业务资源) ,Confirm(真正执行业务,不做任何业务检查,只使用Try阶段预留的业务资源,Confirm操作满足幂等性),Cancel(释放Try阶段预留的业务资源,cancel操作满足幂等性)
觉得应该先看下上面代码和文档中描述的那个买卖的例子:一个简单的购物系统,可以下单,可以选择用红包或余额支付等,从文章中摘出来一张图
基本下单流程就是这样
整个下单的起点就是一个web controller
package org.mengyun.tcctransaction.sample.dubbo.order.web.controller; import org.apache.commons.lang3.tuple.ImmutablePair;
import org.mengyun.tcctransaction.sample.dubbo.order.service.AccountServiceImpl;
import org.mengyun.tcctransaction.sample.order.domain.entity.Order;
import org.mengyun.tcctransaction.sample.order.domain.entity.Product;
import org.mengyun.tcctransaction.sample.order.domain.repository.ProductRepository;
import org.mengyun.tcctransaction.sample.order.domain.service.OrderServiceImpl;
import org.mengyun.tcctransaction.sample.dubbo.order.service.PlaceOrderServiceImpl;
import org.mengyun.tcctransaction.sample.dubbo.order.web.controller.vo.PlaceOrderRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView; import java.math.BigDecimal;
import java.security.InvalidParameterException;
import java.util.List; /**
* Created by changming.xie on 4/1/16.
*/
@Controller
@RequestMapping("")
public class OrderController { @Autowired
PlaceOrderServiceImpl placeOrderService; @Autowired
ProductRepository productRepository; @Autowired
AccountServiceImpl accountService; @Autowired
OrderServiceImpl orderService; @RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index() {
ModelAndView mv = new ModelAndView("/index");
return mv;
} @RequestMapping(value = "/user/{userId}/shop/{shopId}", method = RequestMethod.GET)
public ModelAndView getProductsInShop(@PathVariable long userId,
@PathVariable long shopId) {
List<Product> products = productRepository.findByShopId(shopId);
ModelAndView mv = new ModelAndView("/shop");
mv.addObject("products", products);
mv.addObject("userId", userId);
mv.addObject("shopId", shopId); return mv;
} @RequestMapping(value = "/user/{userId}/shop/{shopId}/product/{productId}/confirm", method = RequestMethod.GET)
public ModelAndView productDetail(@PathVariable long userId,
@PathVariable long shopId,
@PathVariable long productId) { ModelAndView mv = new ModelAndView("product_detail");
mv.addObject("capitalAmount", accountService.getCapitalAccountByUserId(userId));
mv.addObject("redPacketAmount", accountService.getRedPacketAccountByUserId(userId));
mv.addObject("product", productRepository.findById(productId));
mv.addObject("userId", userId);
mv.addObject("shopId", shopId);
return mv;
} @RequestMapping(value = "/placeorder", method = RequestMethod.POST)
public RedirectView placeOrder(@RequestParam String redPacketPayAmount,
@RequestParam long shopId,
@RequestParam long payerUserId,
@RequestParam long productId) {
PlaceOrderRequest request = buildRequest(redPacketPayAmount, shopId, payerUserId, productId);
String merchantOrderNo = placeOrderService.placeOrder(request.getPayerUserId(), request.getShopId(),
request.getProductQuantities(), request.getRedPacketPayAmount());
return new RedirectView("/payresult/" + merchantOrderNo);
} @RequestMapping(value = "/payresult/{merchantOrderNo}", method = RequestMethod.GET)
public ModelAndView getPayResult(@PathVariable String merchantOrderNo) {
ModelAndView mv = new ModelAndView("pay_success");
String payResultTip = null;
Order foundOrder = orderService.findOrderByMerchantOrderNo(merchantOrderNo);
if ("CONFIRMED".equals(foundOrder.getStatus()))
payResultTip = "支付成功";
else if ("PAY_FAILED".equals(foundOrder.getStatus()))
payResultTip = "支付失败";
else
payResultTip = "Unknown";
mv.addObject("payResult", payResultTip);
mv.addObject("capitalAmount", accountService.getCapitalAccountByUserId(foundOrder.getPayerUserId()));
mv.addObject("redPacketAmount", accountService.getRedPacketAccountByUserId(foundOrder.getPayerUserId()));
return mv;
} private PlaceOrderRequest buildRequest(String redPacketPayAmount, long shopId, long payerUserId, long productId) {
BigDecimal redPacketPayAmountInBigDecimal = new BigDecimal(redPacketPayAmount);
if (redPacketPayAmountInBigDecimal.compareTo(BigDecimal.ZERO) < 0)
throw new InvalidParameterException("invalid red packet amount :" + redPacketPayAmount); PlaceOrderRequest request = new PlaceOrderRequest();
request.setPayerUserId(payerUserId);
request.setShopId(shopId);
request.setRedPacketPayAmount(new BigDecimal(redPacketPayAmount));
request.getProductQuantities().add(new ImmutablePair<Long, Integer>(productId, 1));
return request;
}
}
下单
package org.mengyun.tcctransaction.sample.http.order.service; import org.apache.commons.lang3.tuple.Pair;
import org.mengyun.tcctransaction.CancellingException;
import org.mengyun.tcctransaction.ConfirmingException;
import org.mengyun.tcctransaction.sample.order.domain.entity.Order;
import org.mengyun.tcctransaction.sample.order.domain.entity.Shop;
import org.mengyun.tcctransaction.sample.order.domain.repository.ShopRepository;
import org.mengyun.tcctransaction.sample.order.domain.service.OrderServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List; /**
* Created by changming.xie on 4/1/16.
*/
@Service
public class PlaceOrderServiceImpl { @Autowired
ShopRepository shopRepository; @Autowired
OrderServiceImpl orderService; @Autowired
PaymentServiceImpl paymentService; public String placeOrder(long payerUserId, long shopId, List<Pair<Long, Integer>> productQuantities, BigDecimal redPacketPayAmount) {
Shop shop = shopRepository.findById(shopId);
Order order = orderService.createOrder(payerUserId, shop.getOwnerUserId(), productQuantities);
Boolean result = false;
try {
paymentService.makePayment(order, redPacketPayAmount, order.getTotalAmount().subtract(redPacketPayAmount));
} catch (ConfirmingException confirmingException) {
//exception throws with the tcc transaction status is CONFIRMING,
//when tcc transaction is confirming status,
// the tcc transaction recovery will try to confirm the whole transaction to ensure eventually consistent.
result = true;
} catch (CancellingException cancellingException) {
//exception throws with the tcc transaction status is CANCELLING,
//when tcc transaction is under CANCELLING status,
// the tcc transaction recovery will try to cancel the whole transaction to ensure eventually consistent.
} catch (Throwable e) {
//other exceptions throws at TRYING stage.
//you can retry or cancel the operation.
e.printStackTrace();
}
return order.getMerchantOrderNo();
}
}
支付服务,这里开始就用到了TCC
@Compensable(confirmMethod = "confirmMakePayment", cancelMethod = "cancelMakePayment", asyncConfirm = true)
package org.mengyun.tcctransaction.sample.dubbo.order.service; import org.apache.commons.lang3.time.DateFormatUtils;
import org.mengyun.tcctransaction.api.Compensable;
import org.mengyun.tcctransaction.sample.dubbo.capital.api.CapitalTradeOrderService;
import org.mengyun.tcctransaction.sample.dubbo.capital.api.dto.CapitalTradeOrderDto;
import org.mengyun.tcctransaction.sample.dubbo.redpacket.api.RedPacketTradeOrderService;
import org.mengyun.tcctransaction.sample.dubbo.redpacket.api.dto.RedPacketTradeOrderDto;
import org.mengyun.tcctransaction.sample.order.domain.entity.Order;
import org.mengyun.tcctransaction.sample.order.domain.repository.OrderRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.Calendar;
/**
* Created by changming.xie on 4/1/16.
*/
@Service
public class PaymentServiceImpl { @Autowired
CapitalTradeOrderService capitalTradeOrderService; @Autowired
RedPacketTradeOrderService redPacketTradeOrderService; @Autowired
OrderRepository orderRepository; @Compensable(confirmMethod = "confirmMakePayment", cancelMethod = "cancelMakePayment", asyncConfirm = true)
public void makePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {
System.out.println("order try make payment called.time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss")); //check if the order status is DRAFT, if no, means that another call makePayment for the same order happened, ignore this call makePayment.
if (order.getStatus().equals("DRAFT")) {
order.pay(redPacketPayAmount, capitalPayAmount);
try {
orderRepository.updateOrder(order);
} catch (OptimisticLockingFailureException e) {
//ignore the concurrently update order exception, ensure idempotency.
}
} String result = capitalTradeOrderService.record(buildCapitalTradeOrderDto(order));
String result2 = redPacketTradeOrderService.record(buildRedPacketTradeOrderDto(order));
} public void confirmMakePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("order confirm make payment called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
Order foundOrder = orderRepository.findByMerchantOrderNo(order.getMerchantOrderNo());
//check if the trade order status is PAYING, if no, means another call confirmMakePayment happened, return directly, ensure idempotency.
if (foundOrder != null && foundOrder.getStatus().equals("PAYING")) {
order.confirm();
orderRepository.updateOrder(order);
}
} public void cancelMakePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("order cancel make payment called.time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
Order foundOrder = orderRepository.findByMerchantOrderNo(order.getMerchantOrderNo());
//check if the trade order status is PAYING, if no, means another call cancelMakePayment happened, return directly, ensure idempotency.
if (foundOrder != null && foundOrder.getStatus().equals("PAYING")) {
order.cancelPayment();
orderRepository.updateOrder(order);
}
} private CapitalTradeOrderDto buildCapitalTradeOrderDto(Order order) {
CapitalTradeOrderDto tradeOrderDto = new CapitalTradeOrderDto();
tradeOrderDto.setAmount(order.getCapitalPayAmount());
tradeOrderDto.setMerchantOrderNo(order.getMerchantOrderNo());
tradeOrderDto.setSelfUserId(order.getPayerUserId());
tradeOrderDto.setOppositeUserId(order.getPayeeUserId());
tradeOrderDto.setOrderTitle(String.format("order no:%s", order.getMerchantOrderNo()));
return tradeOrderDto;
} private RedPacketTradeOrderDto buildRedPacketTradeOrderDto(Order order) {
RedPacketTradeOrderDto tradeOrderDto = new RedPacketTradeOrderDto();
tradeOrderDto.setAmount(order.getRedPacketPayAmount());
tradeOrderDto.setMerchantOrderNo(order.getMerchantOrderNo());
tradeOrderDto.setSelfUserId(order.getPayerUserId());
tradeOrderDto.setOppositeUserId(order.getPayeeUserId());
tradeOrderDto.setOrderTitle(String.format("order no:%s", order.getMerchantOrderNo()));
return tradeOrderDto;
}
}
继续往下,这里加了一层代理并增加了传播属性的设置,还定义了事物编辑器
@Compensable(propagation = Propagation.SUPPORTS, confirmMethod = "record", cancelMethod = "record", transactionContextEditor = MethodTransactionContextEditor.class)
package org.mengyun.tcctransaction.sample.http.order.service; import org.mengyun.tcctransaction.api.Compensable;
import org.mengyun.tcctransaction.api.Propagation;
import org.mengyun.tcctransaction.api.TransactionContext;
import org.mengyun.tcctransaction.context.MethodTransactionContextEditor;
import org.mengyun.tcctransaction.sample.http.capital.api.CapitalTradeOrderService;
import org.mengyun.tcctransaction.sample.http.capital.api.dto.CapitalTradeOrderDto;
import org.mengyun.tcctransaction.sample.http.redpacket.api.RedPacketTradeOrderService;
import org.mengyun.tcctransaction.sample.http.redpacket.api.dto.RedPacketTradeOrderDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; /**
* Created by changming.xie on 4/19/17.
*/
@Component
public class TradeOrderServiceProxy { @Autowired
CapitalTradeOrderService capitalTradeOrderService; @Autowired
RedPacketTradeOrderService redPacketTradeOrderService; /*the propagation need set Propagation.SUPPORTS,otherwise the recover doesn't work,
The default value is Propagation.REQUIRED, which means will begin new transaction when recover.
*/
@Compensable(propagation = Propagation.SUPPORTS, confirmMethod = "record", cancelMethod = "record", transactionContextEditor = MethodTransactionContextEditor.class)
public String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {
return capitalTradeOrderService.record(transactionContext, tradeOrderDto);
} @Compensable(propagation = Propagation.SUPPORTS, confirmMethod = "record", cancelMethod = "record", transactionContextEditor = MethodTransactionContextEditor.class)
public String record(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto) {
return redPacketTradeOrderService.record(transactionContext, tradeOrderDto);
}
}
继续
package org.mengyun.tcctransaction.sample.dubbo.capital.service; import org.apache.commons.lang3.time.DateFormatUtils;
import org.mengyun.tcctransaction.api.Compensable;
import org.mengyun.tcctransaction.dubbo.context.DubboTransactionContextEditor;
import org.mengyun.tcctransaction.sample.capital.domain.entity.CapitalAccount;
import org.mengyun.tcctransaction.sample.capital.domain.entity.TradeOrder;
import org.mengyun.tcctransaction.sample.capital.domain.repository.CapitalAccountRepository;
import org.mengyun.tcctransaction.sample.capital.domain.repository.TradeOrderRepository;
import org.mengyun.tcctransaction.sample.dubbo.capital.api.CapitalTradeOrderService;
import org.mengyun.tcctransaction.sample.dubbo.capital.api.dto.CapitalTradeOrderDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Calendar; /**
* Created by changming.xie on 4/2/16.
*/
@Service("capitalTradeOrderService")
public class CapitalTradeOrderServiceImpl implements CapitalTradeOrderService {
@Autowired
CapitalAccountRepository capitalAccountRepository; @Autowired
TradeOrderRepository tradeOrderRepository; @Override
@Compensable(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord", transactionContextEditor = DubboTransactionContextEditor.class)
@Transactional
public String record(CapitalTradeOrderDto tradeOrderDto) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("capital try record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
TradeOrder foundTradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());
//check if trade order has been recorded, if yes, return success directly.
if (foundTradeOrder == null) {
TradeOrder tradeOrder = new TradeOrder(
tradeOrderDto.getSelfUserId(),
tradeOrderDto.getOppositeUserId(),
tradeOrderDto.getMerchantOrderNo(),
tradeOrderDto.getAmount()
);
try {
tradeOrderRepository.insert(tradeOrder);
CapitalAccount transferFromAccount = capitalAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());
transferFromAccount.transferFrom(tradeOrderDto.getAmount());
capitalAccountRepository.save(transferFromAccount);
} catch (DataIntegrityViolationException e) {
//this exception may happen when insert trade order concurrently, if happened, ignore this insert operation.
}
}
return "success";
} @Transactional
public void confirmRecord(CapitalTradeOrderDto tradeOrderDto) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("capital confirm record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());
//check if the trade order status is DRAFT, if yes, return directly, ensure idempotency.
if (tradeOrder != null && tradeOrder.getStatus().equals("DRAFT")) {
tradeOrder.confirm();
tradeOrderRepository.update(tradeOrder);
CapitalAccount transferToAccount = capitalAccountRepository.findByUserId(tradeOrderDto.getOppositeUserId());
transferToAccount.transferTo(tradeOrderDto.getAmount());
capitalAccountRepository.save(transferToAccount);
}
} @Transactional
public void cancelRecord(CapitalTradeOrderDto tradeOrderDto) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("capital cancel record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());
//check if the trade order status is DRAFT, if yes, return directly, ensure idempotency.
if (null != tradeOrder && "DRAFT".equals(tradeOrder.getStatus())) {
tradeOrder.cancel();
tradeOrderRepository.update(tradeOrder);
CapitalAccount capitalAccount = capitalAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());
capitalAccount.cancelTransfer(tradeOrderDto.getAmount());
capitalAccountRepository.save(capitalAccount);
}
}
}
示例演示在下完订单后,使用红包帐户和资金帐户来付款,红包帐户服务和资金帐户服务在不同的系统中。示例中,有两个SOA提供方,一个是CapitalTradeOrderService,代表着资金帐户服务,另一个是RedPacketTradeOrderService,代表着红包帐户服务。
下完订单后,订单状态为DRAFT,在TCC事务中TRY阶段,订单支付服务将订单状态变成PAYING,同时远程调用红包帐户服务和资金帐户服务,将付款方的余额减掉(预留业务资源);如果在TRY阶段,任何一个服务失败,tcc-transaction将自动调用这些服务对应的cancel方法,订单支付服务将订单状态变成PAY_FAILED,同时远程调用红包帐户服务和资金帐户服务,将付款方余额减掉的部分增加回去;如果TRY阶段正常完成,则进入CONFIRM阶段,在CONFIRM阶段(tcc-transaction自动调用),订单支付服务将订单状态变成CONFIRMED,同时远程调用红包帐户服务和资金帐户服务对应的CONFIRM方法,将收款方的余额增加。
TCC细读 - 1 例子流程的更多相关文章
- TCC细读 - 3 恢复流程
重试定时任务,通过外部调度实现 package org.mengyun.tcctransaction.spring.recover; import org.mengyun.tcctransaction ...
- TCC细读 - 2 核心实现
TCC,基于业务层面的事物定义,粒度完全由业务自己控制,本质上还是补偿的思路,它把事物运行过程分为try-confirm-cancel阶段,每个阶段逻辑由业务代码控制 业务活动管理器控制业务活动的一致 ...
- FIS3使用官方例子流程
fis3 的常用例子:https://github.com/fex-team/fis3-demo git链接可在页面中获取替换下面的git链接: 例子准备: git clone https://git ...
- activiti并发多实例子流程任务处理
一直在搞工作流(activiti),总结一下关于工作流(activiti)中同时并发处理多个子流程的操作方法. 先说下我要实现的业务: 1.办公室发通知(在系统申报页面上,勾选科室,被选中的科室执行第 ...
- tcc分布式事务框架解析
前言碎语 楼主之前推荐过2pc的分布式事务框架LCN.今天来详细聊聊TCC事务协议. 2pc实现:https://github.com/codingapi/tx-lcn tcc实现:https://g ...
- 分布式事务解决方案汇总:2PC、3PC、消息中间件、TCC、状态机+重试+幂等(转)
数据一致性问题非常多样,下面举一些常见例子.比如在更新数据的时候,先更新了数据库,后更新了缓存,一旦缓存更新失败,此时数据库和缓存数据会不一致.反过来,如果先更新缓存,再更新数据库,一旦缓存更新成功, ...
- Activiti 多个并发子流程的应用
多个部门发起资金计划,最后统一到财务部审批,每个部门发起资金计划是一个子流程,财务部审批是多个部门的计划同时审批,审批完成后,再提交上级领导审批. 流程如下: 要解决以上问题,需要实现多个子流程并行处 ...
- 分布式事务之解决方案(TCC)
4. 分布式事务解决方案之TCC 4.1. 什么是TCC事务 TCC是Try.Confirm.Cancel三个词语的缩写,TCC要求每个分支事务实现三个操作 :预处理Try.确认Confirm.撤销C ...
- 分布式事务二TCC
分布式事务解决方案之TCC 4.1.什么是TCC事务 TCC是Try.Confirm.Cancel三个词语的缩写,TCC要求每个分支事务实现三个操作:预处理Try.确认Confirm.撤销Cancel ...
随机推荐
- 剑指Offer 44. 翻转单词顺序列 (字符串)
题目描述 牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上.同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思.例如,"st ...
- 自动化测试-8.selenium操作元素之键盘和鼠标事件
前言 在前面的几篇中重点介绍了一些元素的定位方法,定位到元素后,接下来就是需要操作元素了.本篇总结了web页面常用的一些操作元素方法,可以统称为行为事件 有些web界面的选项菜单需要鼠标悬停在某个元素 ...
- Locust 集合点
直接编写接口事务脚本对后台接口进行测试:有时测试需要让所有并发用户完成初始化后再进行压力测试,这就需要类似于LoadRunner中的集合点的概念,由于框架本身没有直接封装,有如下办法实现: from ...
- rootfs 制作
最小 根文件系统 (1)/dev/console(终端控制台, 提供标准输入.标准输出以及标准错误) /dev/null (表示空设备终端, 所有写到这个文件中的数据都会被丢弃掉.) (2)init进 ...
- CMake 构建项目教程-简介
CMake 构建项目教程-简介 Linux 平台构建项目,选择了CLion作为C++的IDE,而CLion默认就是使用CMake构建项目,所以这里记录了CMake在构建项目过程的一些小知识. 1. 项 ...
- idea快捷键的设置
因为开始学java被培训机构里的无良老师给带偏,"染上了"MyEclipse的快捷键"恶习",于是很难改了. 所以,在使用idea时,要设置快捷键了,快点适应之 ...
- git 本地分支和远程分支改名字
1.将本地分支进行改名: git branch -m old_branch new_branch 2.将本地分支的远程分支删除: git push origin :old_branch 3.将改名后的 ...
- hdu 1166 (单点更新+区间求和+裸题)
敌兵布阵 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submissi ...
- 《Linux内核原理与分析》第七周作业
课本:第六章 进程的描述和进程的创建 操作系统内核实现操作系统的三大管理功能 进程管理 内存管理 文件系统 在操作系统原理中,通过进程控制块PCB描述进程:在Linux内核中,通过一个数据结构stru ...
- DRBD搭建
基于块设备在不同的高可用服务器之间同步和镜像数据的软件,块设备可以是磁盘分区,LVM逻辑卷或整块磁盘,解决磁盘单点故障 三种复制协议 (1)协议A:异步复制协议,本地写成功后立即返回,数据放在发送bu ...