【7】JMicro分布式事务Demo

本文先从使用者角度说明JMicro分布式事务的使用方式,下一篇将再解析JMicro分布式事务实现细节。

 1. 部署图

1)图中每个结点表示一个进程实例,可以部署于同一物理机或者不同物理机,网络能联通即可;

2)图中每个进程实例可启用主备服务,图中除Api网关实例外都只画主实例;

3)ZK及Redis被全部JVM实例依赖,图中没有一一连线;

4)LogServer,Repository,Security被其他(包括其自身)全部JVM依赖,以分别提供日志,资源,安全服务;

5)此Demo我们只关注Shop,Order,Payment这3个类型服务;

6)Shop,Order,Payment可以同时存在多个实例或多个主从实例同时提供服务,此样例只使用一个作为Demo;

 

2. 核心接口及实现类图

1)服务方法名带Asy后缀表示异步方法,与相应不带Asy方法名实现同样功能,只有同步和异步区别;

2)3个服务实现分别部署于Shop,Order,Payment 3个类型的JVM,3者之前通过RPC远程调用交互;

 

3. 店铺服务实现

Shop服务实现代码GitHub路径如下:

https://github.com/mynewworldyyl/jmicro/blob/master/example/expjmicro.tx/expjmicro.tx.shop/src/main/java/cn/expjmicro/tx/shop/TxShopServiceImpl.java

其中异步方法代码如下:

 1     /**
 2      * 异步实现下单服务
 3      * @see txType=TxConstants.TYPE_TX_DISTRIBUTED 启用分布式服务
 4      * @see txPhase=TxConstants.TX_3PC 3PC事务提交方案
 5      * @see txIsolation=Connection.TRANSACTION_READ_COMMITTED 事务隔离级别为读提交
 6      * 
 7      */
 8     @Override
 9     @SMethod(txType=TxConstants.TYPE_TX_DISTRIBUTED,timeout=3*60*1000,
10     txIsolation=Connection.TRANSACTION_READ_COMMITTED,txPhase=TxConstants.TX_3PC)
11     public IPromise<Resp<Boolean>> buyAsy(int goodId,int num) {
12         
13         PromiseImpl<Resp<Boolean>> p = new PromiseImpl<>();
14         
15         Resp<Boolean> r = new Resp<>(Resp.CODE_FAIL,false);
16         p.setResult(r);
17         
18         Good g = goodMapper.selectById(goodId);
19         if(g == null) {
20             r.setMsg("Good not found!");
21             p.done();
22             return p;
23         }
24         
25         if(g.getUsableCnt() < num) {//判断是否还有充足库存
26             r.setMsg("Good num not egnogh!");
27             p.done();
28             return p;
29         }
30         
31         if(LG.isLoggable(MC.LOG_INFO)) {
32             String msg = "decGoodNum cur: "+g.getUsableCnt()+" txid: " + 
33                     (JMicroContext.get().getLong(TxConstants.TYPE_TX_KEY, -1L));
34             LG.log(MC.LOG_INFO, TxShopServiceImpl.class, msg);
35             logger.info(msg);
36         }
37         
38         int un = goodMapper.decGoodNum(goodId, num);//扣减库存
39         if(un < 1) {
40             r.setMsg("fail to update good num!");
41             LG.log(MC.LOG_ERROR, TxShopServiceImpl.class, "Fail to dec good num cur:" + g.getUsableCnt());
42             p.done();
43             return p;
44         }
45         
46         Req req = new Req();
47         req.setGoodId(goodId);
48         req.setNum(num);
49         req.setTxid(JMicroContext.get().getLong(TxConstants.TYPE_TX_KEY, -1L));
50         reqMapper.saveReq(req);
51         
52         if(LG.isLoggable(MC.LOG_INFO)) {
53             String msg = "Invoke order service takeOrderAsy txid: " + 
54                     (JMicroContext.get().getLong(TxConstants.TYPE_TX_KEY, -1L));
55             LG.log(MC.LOG_INFO, TxShopServiceImpl.class, msg);
56             logger.info(msg);
57         }
58         return orderSrv.takeOrderAsy(g,num);//调用远程下单服务
59     }
SMethod注解与事务相关3个属性为txType,txPhase和txIsolation。
txType表示事务类型,默认为不启用事务,值分别为TYPE_TX_NO不支持事务,TYPE_TX_LOCAL本地事务,TYPE_TX_DISTRIBUTED分布式事务
txPhase表示事务分段,默认2PC,目前支持2PC及3PC,3PC在2PC基础上,在事务提交之前,询问事务参与者是否可以提交,而2PC只要参与者全部投票通过后就直接提交,回滚不需要3PC。
txIsolation表示事务隔离级别,与JDBC定义一致。分别有TRANSACTION_READ_UNCOMMITTED读未提交,TRANSACTION_READ_COMMITTED读提交,
TRANSACTION_REPEATABLE_READ可重复读,TRANSACTION_SERIALIZABLE序列化事务。默认为TRANSACTION_READ_COMMITTED


4. 订单服务实现
GitHub路径如下:

https://github.com/mynewworldyyl/jmicro/blob/master/example/expjmicro.tx/expjmicro.tx.order/src/main/java/cn/expjmicro/tx/order/TxOrderServiceImpl.java
其中异步方法代码如下:
 1 /**
 2      * 异步下单服务, 被商店服务调用
 3      * TxConstants.TYPE_TX_DISTRIBUTED 启用分布式服务
 4      */
 5     @Override
 6     @SMethod(txType=TxConstants.TYPE_TX_DISTRIBUTED)
 7     public IPromise<Resp<Boolean>> takeOrderAsy(Good g,int num) {
 8         
 9         Order o = new Order();
10         o.setGoodId(g.getId());
11         o.setNum(num);
12         o.setAmount(o.getNum()*g.getPrice());
13         o.setId(idServer.getLongId(Order.class));
14         o.setTxid(JMicroContext.get().getLong(TxConstants.TYPE_TX_KEY, -1L));
15         
16         LG.log(MC.LOG_INFO, this.getClass(), "Save order");
17         //保存订单
18         om.saveOrder(o);
19         
20         Payment p = new Payment();
21         p.setId(idServer.getLongId(Order.class));
22         p.setAmount(o.getAmount());
23         p.setOrderId(o.getId());
24         p.setTxid(o.getTxid());
25         
26         LG.log(MC.LOG_INFO, this.getClass(), "Before invoke pay service");
27         
28         //调用支付服务
29         return paymentSrv.payAsy(p);
30     }
5. 支付服务实现
GitHub路径如下:

https://github.com/mynewworldyyl/jmicro/blob/master/example/expjmicro.tx/expjmicro.tx.payment/src/main/java/cn/expjmicro/tx/payment/TxPaymentServiceImpl.java
其中异步方法代码如下:
/**
     * 异步实现支付服务
     */
    @Override
    @SMethod(txType=TxConstants.TYPE_TX_DISTRIBUTED)
    public IPromise<Resp<Boolean>> payAsy(Payment p) {
        Resp<Boolean> r = pay(p);
        PromiseImpl<Resp<Boolean>> pr = new PromiseImpl<>();
        pr.setResult(r);
        pr.done();
        return pr;
    }

6. 运行

1)确保下载完整代码

https://github.com/mynewworldyyl/jmicro

2)构建

mvn clean install -Dmaven.test.skip=true

3)Eclipse工具运行项目配置,运行全部项目此配置都相同,后面不再一一列举

 

VM参数配置javaagent,运行全部项目此配置都相同,后面不再一一列举

 

 -javaagent:D:\opensource\github\jmicro\agent\target\jmicro.agent-0.0.2-SNAPSHOT.jar

 

 4)运行Security配置

 

 对应文本

-DsysLogLevel=3 -DclientId=0 -DadminClientId=0  -Dlog4j.configuration=../../log4j.xml  -Dpwd=0 -DpriKeyPwd=私钥密码 -DinstanceName=security

 

5)运行MNG管理后台后

 

 参数文本

-DsysLogLevel=4  -DclientId=0 -DadminClientId=0 -DpriKeyPwd=私钥密码  -D/mongodb/username=MongoDB用户名 -D/mongodb/password=MONGODB密码 -Dlog4j.configuration=../../log4j.xml -Dpwd=0

 

6)运行API网关

程序参数(Program arguments)

-DinstanceName=apigateway  -DexportHttpIP=192.168.56.1  -DexportSocketIP=192.168.56.1  -DlistenHttpIP=0.0.0.0  -DlistenSocketIP=0.0.0.0 -D/NettySocketServer/nettyPort=9092 -D/nettyHttpPort=9090  -DapiGatewayExportHttpIP=0.0.0.0 -DinitGatewayAndMng=false -D/StaticResourceHttpHandler/staticResourceRoot_mng=D:/opensource/github/jmicro/mng.web/public  -DclientId=0 -DadminClientId=0 -DsysLogLevel=5  -DpriKeyPwd=API网关私钥 -Dlog4j.configuration=../../log4j.xml -Dpwd=0

exportHttpIP:外网要访问API网关的HTTP IP地址,因为我们测试环境,只需要一个IP即可;

exportSocketIP:外网要访问API网关的SOCKET地址,因为我们测试环境,只需要一个IP即可;

/NettySocketServer/nettyPort:Socket端口

/nettyHttpPort: HTTP端口

/StaticResourceHttpHandler/staticResourceRoot_mng:后台管理页面地址,本地测试可以直接NPM启动页面

priKeyPwd:API网关SSL私钥密码,参考别的章节说明相关配置方式

 

7)日志监控服务

-DsysLogLevel=4  -DclientId=0 -DadminClientId=0 -Dlog4j.configuration=../../log4j.xml -D/mongodb/username=MongoDB用户名 -D/mongodb/password=MONGODB密码  -Dpwd=0

 

8)事务协调器

-DsysLogLevel=3 -DclientId=0 -DadminClientId=0  -Dlog4j.configuration=../../log4j.xml  -Dpwd=0

 

9)店铺服务

-DsysLogLevel=4  -DclientId=25500 -DadminClientId=0  -Dlog4j.configuration=../../../log4j.xml -Dpwd=1

此时clientId=25500表示以普通账号启动用户级服务,clientId=0表示以高级账号启动系统级的服务,两种服务资源隔离

 

10) 订单服务

-DsysLogLevel=4  -DclientId=25500 -DadminClientId=0  -Dlog4j.configuration=../../../log4j.xml -Dpwd=1

 

11) 支付服务

-DsysLogLevel=4  -DclientId=25500 -DadminClientId=0 -Dlog4j.configuration=../../../log4j.xml -Dpwd=1

 

12)启动管理后台页面

命令行进入D:\opensource\github\jmicro\mng.web目录 ,运行

npm run serve

 

13) 浏览器打开

http://localhost:8081/,如下图

 

 在此页面调用一下Shop服务的buyAsy方法,并查看事务日志

选择主菜单“监控/服务”,弹出如下侧栏服务列表,并按红框选择buyAsy方法

 

 编辑区往下拖,看到到如下卡片

 

 在Testing Args输入框输入[1,1],表示buyASy方法两个参数值,第一个是商品ID,第二个是购买数量

点“Start”,将发起服务方法的调用,在Testting Result输出[object Object]

 

14)查看调用日志

选择主菜单“监控/监听日志”打开日志视图,如下图

 

 在打开的视图将鼠标移到最左侧,自动显示查询条件输入框,输入如下红框内容,并点“Query”,如下图

 

 在日志视图看到事务相关日志,注意,最前面日志最新,如要看开始事务日志,要滚动到视图最下面,如下图

 

 提交事图日志

 

 

15)链路日志

现在想看看刚发起的购买动作相关的整个调用链路日志,而不是离散的日志,选择主菜单“监控/调用链”,如下图

 

 视图区将打开如下图

 

 最左边为每个RPC调用的唯一标识,每个调用花费时间。点最右边的“Detail”按钮,可以查看更详细的信息,如下图

 

基于JMicro开发的服务除了可以独立运行JVM外,还可以托管到jmicro.cn平台,实现自动化部署和运维,避免传统购买云服务及日常运维的高成本开销。

Github:https://github.com/mynewworldyyl/jmicro

Demo: http://jmicro.cn/

 

posted @ 2021-04-19 14:37  JMICRO  阅读(148)  评论(0编辑  收藏  举报