| <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Apache Dubbo – 微服务生态</title><link>https://dubbo.apache.org/zh-cn/overview/tasks/ecosystem/</link><description>Recent content in 微服务生态 on Apache Dubbo</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><atom:link href="https://dubbo.apache.org/zh-cn/overview/tasks/ecosystem/index.xml" rel="self" type="application/rss+xml"/><item><title>Overview: 事务管理</title><link>https://dubbo.apache.org/zh-cn/overview/tasks/ecosystem/transaction/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/overview/tasks/ecosystem/transaction/</guid><description> |
| <h2 id="seata-是什么">Seata 是什么</h2> |
| <p>Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。</p> |
| <h2 id="一示例架构说明">一、示例架构说明</h2> |
| <p>可在此查看本示例完整代码地址:<a href="https://github.com/apache/dubbo-samples/tree/master/2-advanced/dubbo-samples-seata" target="_blank">dubbo-samples-seata</a></p> |
| <p>用户采购商品业务,整个业务包含3个微服务:</p> |
| <ul> |
| <li>库存服务: 扣减给定商品的库存数量。</li> |
| <li>订单服务: 根据采购请求生成订单。</li> |
| <li>账户服务: 用户账户金额扣减。</li> |
| </ul> |
| <p><img src="https://dubbo.apache.org/imgs/docs3-v2/java-sdk/seata/transaction-1.png" alt="image.png"></p> |
| <h3 id="storageservice">StorageService</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">StorageService</span> { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * 扣除存储数量 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">deduct</span>(String commodityCode, <span style="color:#dc322f">int</span> count); |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h3 id="orderservice">OrderService</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">OrderService</span> { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * 创建订单 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> Order <span style="color:#268bd2">create</span>(String userId, String commodityCode, <span style="color:#dc322f">int</span> orderCount); |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h3 id="accountservice">AccountService</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">AccountService</span> { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * 从用户账户中借出 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">debit</span>(String userId, <span style="color:#dc322f">int</span> money); |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h2 id="二主要的业务逻辑">二、主要的业务逻辑</h2> |
| <h3 id="businessservice">BusinessService</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">BusinessServiceImpl</span> <span style="color:#268bd2">implements</span> BusinessService { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> StorageService storageService; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> OrderService orderService; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * 采购 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> */</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">purchase</span>(String userId, String commodityCode, <span style="color:#dc322f">int</span> orderCount) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 扣除存储数量</span> |
| </span></span><span style="display:flex;"><span> storageService.deduct(commodityCode, orderCount); |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 创建订单</span> |
| </span></span><span style="display:flex;"><span> orderService.create(userId, commodityCode, orderCount); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h3 id="storageservice-1">StorageService</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">StorageServiceImpl</span> <span style="color:#268bd2">implements</span> StorageService { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> JdbcTemplate jdbcTemplate; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">deduct</span>(String commodityCode, <span style="color:#dc322f">int</span> count) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 修改数据库:扣减存储数量</span> |
| </span></span><span style="display:flex;"><span> jdbcTemplate.update(<span style="color:#2aa198">&#34;update storage_tbl set count = count - ? where commodity_code = ?&#34;</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">new</span> Object<span style="color:#719e07">[]</span>{count, commodityCode}); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h3 id="orderservice-1">OrderService</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">OrderServiceImpl</span> <span style="color:#268bd2">implements</span> OrderService { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> AccountService accountService; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> JdbcTemplate jdbcTemplate; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Order <span style="color:#268bd2">create</span>(String userId, String commodityCode, <span style="color:#dc322f">int</span> orderCount) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 计算金额</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#dc322f">int</span> orderMoney <span style="color:#719e07">=</span> calculate(commodityCode, orderCount); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 用户账户中扣减金额</span> |
| </span></span><span style="display:flex;"><span> accountService.debit(userId, orderMoney); |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 修改数据库:新建订单</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">final</span> Order order <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Order(); |
| </span></span><span style="display:flex;"><span> order.userId <span style="color:#719e07">=</span> userId; |
| </span></span><span style="display:flex;"><span> order.commodityCode <span style="color:#719e07">=</span> commodityCode; |
| </span></span><span style="display:flex;"><span> order.count <span style="color:#719e07">=</span> orderCount; |
| </span></span><span style="display:flex;"><span> order.money <span style="color:#719e07">=</span> orderMoney; |
| </span></span><span style="display:flex;"><span> KeyHolder keyHolder <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> GeneratedKeyHolder(); |
| </span></span><span style="display:flex;"><span> jdbcTemplate.update(con <span style="color:#719e07">-&gt;</span> { |
| </span></span><span style="display:flex;"><span> PreparedStatement pst <span style="color:#719e07">=</span> con.prepareStatement( |
| </span></span><span style="display:flex;"><span> <span style="color:#2aa198">&#34;insert into order_tbl (user_id, commodity_code, count, money) values (?, ?, ?, ?)&#34;</span>, |
| </span></span><span style="display:flex;"><span> PreparedStatement.RETURN_GENERATED_KEYS); |
| </span></span><span style="display:flex;"><span> pst.setObject(1, order.userId); |
| </span></span><span style="display:flex;"><span> pst.setObject(2, order.commodityCode); |
| </span></span><span style="display:flex;"><span> pst.setObject(3, order.count); |
| </span></span><span style="display:flex;"><span> pst.setObject(4, order.money); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> pst; |
| </span></span><span style="display:flex;"><span> }, keyHolder); |
| </span></span><span style="display:flex;"><span> order.id <span style="color:#719e07">=</span> keyHolder.getKey().longValue(); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> order; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h3 id="accountservice-1">AccountService</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">AccountServiceImpl</span> <span style="color:#268bd2">implements</span> AccountService { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> JdbcTemplate jdbcTemplate; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">debit</span>(String userId, <span style="color:#dc322f">int</span> money) { |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">// 修改数据库:用户账户中扣减金额 </span> |
| </span></span><span style="display:flex;"><span> jdbcTemplate.update(<span style="color:#2aa198">&#34;update account_tbl set money = money - ? where user_id = ?&#34;</span>, <span style="color:#719e07">new</span> Object<span style="color:#719e07">[]</span>{money, userId}); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><h2 id="三快速启动示例">三、快速启动示例</h2> |
| <h3 id="step-1-下载源码">Step 1: 下载源码</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>git clone -b master https://github.com/apache/dubbo-samples.git |
| </span></span><span style="display:flex;"><span><span style="color:#b58900">cd</span> ./dubbo-samples-transaction/ |
| </span></span></code></pre></div><h3 id="step-2-通过-docker-compose-启动-seata-server-和-mysql-等">Step 2: 通过 docker-compose 启动 Seata-Server 和 MySQL 等</h3> |
| <p>在此示例中,我们使用 docker-compose 快速拉起 seata-server 和 mysql 等服务。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#b58900">cd</span> src/main/resources/docker |
| </span></span><span style="display:flex;"><span>docker-compose up |
| </span></span></code></pre></div><h3 id="step-3-构建用例">Step 3: 构建用例</h3> |
| <p>执行 maven 命令,打包 demo 工程</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mvn clean package |
| </span></span></code></pre></div><h3 id="step-4-启动-accountservice">Step 4: 启动 AccountService</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>java <span style="color:#719e07">-</span>classpath .<span style="color:#719e07">/</span>target<span style="color:#719e07">/</span>dubbo<span style="color:#719e07">-</span>samples<span style="color:#719e07">-</span>transaction<span style="color:#719e07">-</span>1.0<span style="color:#719e07">-</span>SNAPSHOT.jar org.apache.dubbo.samples.starter.DubboAccountServiceStarter |
| </span></span></code></pre></div><h3 id="step-5-启动-orderservice">Step 5: 启动 OrderService</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>java <span style="color:#719e07">-</span>classpath .<span style="color:#719e07">/</span>target<span style="color:#719e07">/</span>dubbo<span style="color:#719e07">-</span>samples<span style="color:#719e07">-</span>transaction<span style="color:#719e07">-</span>1.0<span style="color:#719e07">-</span>SNAPSHOT.jar org.apache.dubbo.samples.starter.DubboOrderServiceStarter |
| </span></span></code></pre></div><h3 id="step-6-启动-storageservice">Step 6: 启动 StorageService</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>java <span style="color:#719e07">-</span>classpath .<span style="color:#719e07">/</span>target<span style="color:#719e07">/</span>dubbo<span style="color:#719e07">-</span>samples<span style="color:#719e07">-</span>transaction<span style="color:#719e07">-</span>1.0<span style="color:#719e07">-</span>SNAPSHOT.jar org.apache.dubbo.samples.starter.DubboStorageServiceStarter |
| </span></span></code></pre></div><h3 id="step-7-启动-businessservice">Step 7: 启动 BusinessService</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>java <span style="color:#719e07">-</span>classpath .<span style="color:#719e07">/</span>target<span style="color:#719e07">/</span>dubbo<span style="color:#719e07">-</span>samples<span style="color:#719e07">-</span>transaction<span style="color:#719e07">-</span>1.0<span style="color:#719e07">-</span>SNAPSHOT.jar org.apache.dubbo.samples.starter.DubboBusinessTester |
| </span></span></code></pre></div><h2 id="四示例核心流程">四、示例核心流程</h2> |
| <p><img src="https://dubbo.apache.org/imgs/docs3-v2/java-sdk/seata/transaction-2.png" alt="image.png"></p> |
| <h3 id="step-1-修改业务代码">Step 1: 修改业务代码</h3> |
| <p>此处仅仅需要一行注解 <code>@GlobalTransactional</code> 写在业务发起方的方法上:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span> <span style="color:#268bd2">@GlobalTransactional</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> <span style="color:#dc322f">void</span> <span style="color:#268bd2">purchase</span>(String userId, String commodityCode, <span style="color:#dc322f">int</span> orderCount) { |
| </span></span><span style="display:flex;"><span> ...... |
| </span></span><span style="display:flex;"><span> } |
| </span></span></code></pre></div><h3 id="step-2-安装数据库">Step 2: 安装数据库</h3> |
| <ul> |
| <li>要求: MySQL (InnoDB 存储引擎)。</li> |
| </ul> |
| <p><strong>提示:</strong> 事实上例子中3个微服务需要3个独立的数据库,但为了方便我们使用同一物理库并配置3个逻辑连接串。</p> |
| <p>更改以下xml文件中的数据库url、username和password</p> |
| <p>dubbo-account-service.xml |
| dubbo-order-service.xml |
| dubbo-storage-service.xml</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span> <span style="color:#268bd2">&lt;property</span> name=<span style="color:#2aa198">&#34;url&#34;</span> value=<span style="color:#2aa198">&#34;jdbc:mysql://x.x.x.x:3306/xxx&#34;</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;property</span> name=<span style="color:#2aa198">&#34;username&#34;</span> value=<span style="color:#2aa198">&#34;xxx&#34;</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">&lt;property</span> name=<span style="color:#2aa198">&#34;password&#34;</span> value=<span style="color:#2aa198">&#34;xxx&#34;</span> <span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><h3 id="step-3-为-seata-创建-undo_log-表">Step 3: 为 Seata 创建 undo_log 表</h3> |
| <p><code>UNDO_LOG</code> 此表用于 Seata 的AT模式。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#586e75">-- 注意当 Seata 版本升级至 0.3.0+ 将由之前的普通索引变更为唯一索引。 |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"></span><span style="color:#719e07">CREATE</span> <span style="color:#719e07">TABLE</span> <span style="color:#719e07">`</span>undo_log<span style="color:#719e07">`</span> ( |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span>id<span style="color:#719e07">`</span> <span style="color:#b58900">bigint</span>(<span style="color:#2aa198">20</span>) <span style="color:#719e07">NOT</span> <span style="color:#719e07">NULL</span> AUTO_INCREMENT, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span>branch_id<span style="color:#719e07">`</span> <span style="color:#b58900">bigint</span>(<span style="color:#2aa198">20</span>) <span style="color:#719e07">NOT</span> <span style="color:#719e07">NULL</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span>xid<span style="color:#719e07">`</span> <span style="color:#b58900">varchar</span>(<span style="color:#2aa198">100</span>) <span style="color:#719e07">NOT</span> <span style="color:#719e07">NULL</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span>context<span style="color:#719e07">`</span> <span style="color:#b58900">varchar</span>(<span style="color:#2aa198">128</span>) <span style="color:#719e07">NOT</span> <span style="color:#719e07">NULL</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span>rollback_info<span style="color:#719e07">`</span> longblob <span style="color:#719e07">NOT</span> <span style="color:#719e07">NULL</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span>log_status<span style="color:#719e07">`</span> <span style="color:#b58900">int</span>(<span style="color:#2aa198">11</span>) <span style="color:#719e07">NOT</span> <span style="color:#719e07">NULL</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span>log_created<span style="color:#719e07">`</span> datetime <span style="color:#719e07">NOT</span> <span style="color:#719e07">NULL</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span>log_modified<span style="color:#719e07">`</span> datetime <span style="color:#719e07">NOT</span> <span style="color:#719e07">NULL</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span>ext<span style="color:#719e07">`</span> <span style="color:#b58900">varchar</span>(<span style="color:#2aa198">100</span>) <span style="color:#719e07">DEFAULT</span> <span style="color:#719e07">NULL</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">PRIMARY</span> <span style="color:#719e07">KEY</span> (<span style="color:#719e07">`</span>id<span style="color:#719e07">`</span>), |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">UNIQUE</span> <span style="color:#719e07">KEY</span> <span style="color:#719e07">`</span>ux_undo_log<span style="color:#719e07">`</span> (<span style="color:#719e07">`</span>xid<span style="color:#719e07">`</span>,<span style="color:#719e07">`</span>branch_id<span style="color:#719e07">`</span>) |
| </span></span><span style="display:flex;"><span>) ENGINE<span style="color:#719e07">=</span>InnoDB AUTO_INCREMENT<span style="color:#719e07">=</span><span style="color:#2aa198">1</span> <span style="color:#719e07">DEFAULT</span> CHARSET<span style="color:#719e07">=</span>utf8; |
| </span></span></code></pre></div><h3 id="step-4-创建相关业务表">Step 4: 创建相关业务表</h3> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">DROP</span> <span style="color:#719e07">TABLE</span> <span style="color:#719e07">IF</span> <span style="color:#719e07">EXISTS</span> <span style="color:#719e07">`</span>storage_tbl<span style="color:#719e07">`</span>; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">CREATE</span> <span style="color:#719e07">TABLE</span> <span style="color:#719e07">`</span>storage_tbl<span style="color:#719e07">`</span> ( |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span>id<span style="color:#719e07">`</span> <span style="color:#b58900">int</span>(<span style="color:#2aa198">11</span>) <span style="color:#719e07">NOT</span> <span style="color:#719e07">NULL</span> AUTO_INCREMENT, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span>commodity_code<span style="color:#719e07">`</span> <span style="color:#b58900">varchar</span>(<span style="color:#2aa198">255</span>) <span style="color:#719e07">DEFAULT</span> <span style="color:#719e07">NULL</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span><span style="color:#719e07">count</span><span style="color:#719e07">`</span> <span style="color:#b58900">int</span>(<span style="color:#2aa198">11</span>) <span style="color:#719e07">DEFAULT</span> <span style="color:#2aa198">0</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">PRIMARY</span> <span style="color:#719e07">KEY</span> (<span style="color:#719e07">`</span>id<span style="color:#719e07">`</span>), |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">UNIQUE</span> <span style="color:#719e07">KEY</span> (<span style="color:#719e07">`</span>commodity_code<span style="color:#719e07">`</span>) |
| </span></span><span style="display:flex;"><span>) ENGINE<span style="color:#719e07">=</span>InnoDB <span style="color:#719e07">DEFAULT</span> CHARSET<span style="color:#719e07">=</span>utf8; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">DROP</span> <span style="color:#719e07">TABLE</span> <span style="color:#719e07">IF</span> <span style="color:#719e07">EXISTS</span> <span style="color:#719e07">`</span>order_tbl<span style="color:#719e07">`</span>; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">CREATE</span> <span style="color:#719e07">TABLE</span> <span style="color:#719e07">`</span>order_tbl<span style="color:#719e07">`</span> ( |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span>id<span style="color:#719e07">`</span> <span style="color:#b58900">int</span>(<span style="color:#2aa198">11</span>) <span style="color:#719e07">NOT</span> <span style="color:#719e07">NULL</span> AUTO_INCREMENT, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span>user_id<span style="color:#719e07">`</span> <span style="color:#b58900">varchar</span>(<span style="color:#2aa198">255</span>) <span style="color:#719e07">DEFAULT</span> <span style="color:#719e07">NULL</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span>commodity_code<span style="color:#719e07">`</span> <span style="color:#b58900">varchar</span>(<span style="color:#2aa198">255</span>) <span style="color:#719e07">DEFAULT</span> <span style="color:#719e07">NULL</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span><span style="color:#719e07">count</span><span style="color:#719e07">`</span> <span style="color:#b58900">int</span>(<span style="color:#2aa198">11</span>) <span style="color:#719e07">DEFAULT</span> <span style="color:#2aa198">0</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span>money<span style="color:#719e07">`</span> <span style="color:#b58900">int</span>(<span style="color:#2aa198">11</span>) <span style="color:#719e07">DEFAULT</span> <span style="color:#2aa198">0</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">PRIMARY</span> <span style="color:#719e07">KEY</span> (<span style="color:#719e07">`</span>id<span style="color:#719e07">`</span>) |
| </span></span><span style="display:flex;"><span>) ENGINE<span style="color:#719e07">=</span>InnoDB <span style="color:#719e07">DEFAULT</span> CHARSET<span style="color:#719e07">=</span>utf8; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">DROP</span> <span style="color:#719e07">TABLE</span> <span style="color:#719e07">IF</span> <span style="color:#719e07">EXISTS</span> <span style="color:#719e07">`</span>account_tbl<span style="color:#719e07">`</span>; |
| </span></span><span style="display:flex;"><span><span style="color:#719e07">CREATE</span> <span style="color:#719e07">TABLE</span> <span style="color:#719e07">`</span>account_tbl<span style="color:#719e07">`</span> ( |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span>id<span style="color:#719e07">`</span> <span style="color:#b58900">int</span>(<span style="color:#2aa198">11</span>) <span style="color:#719e07">NOT</span> <span style="color:#719e07">NULL</span> AUTO_INCREMENT, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span>user_id<span style="color:#719e07">`</span> <span style="color:#b58900">varchar</span>(<span style="color:#2aa198">255</span>) <span style="color:#719e07">DEFAULT</span> <span style="color:#719e07">NULL</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">`</span>money<span style="color:#719e07">`</span> <span style="color:#b58900">int</span>(<span style="color:#2aa198">11</span>) <span style="color:#719e07">DEFAULT</span> <span style="color:#2aa198">0</span>, |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">PRIMARY</span> <span style="color:#719e07">KEY</span> (<span style="color:#719e07">`</span>id<span style="color:#719e07">`</span>) |
| </span></span><span style="display:flex;"><span>) ENGINE<span style="color:#719e07">=</span>InnoDB <span style="color:#719e07">DEFAULT</span> CHARSET<span style="color:#719e07">=</span>utf8; |
| </span></span></code></pre></div><h3 id="step-5-启动-seata-server-服务">Step 5: 启动 Seata-Server 服务</h3> |
| <ul> |
| <li>下载<a href="https://github.com/seata/seata/releases">服务器软件包</a>,将其解压缩。</li> |
| </ul> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>Usage: sh seata-server.sh<span style="color:#719e07">(</span><span style="color:#719e07">for</span> linux and mac<span style="color:#719e07">)</span> or cmd seata-server.bat<span style="color:#719e07">(</span><span style="color:#719e07">for</span> windows<span style="color:#719e07">)</span> <span style="color:#719e07">[</span>options<span style="color:#719e07">]</span> |
| </span></span><span style="display:flex;"><span> Options: |
| </span></span><span style="display:flex;"><span> --host, -h |
| </span></span><span style="display:flex;"><span> The host to bind. |
| </span></span><span style="display:flex;"><span> Default: 0.0.0.0 |
| </span></span><span style="display:flex;"><span> --port, -p |
| </span></span><span style="display:flex;"><span> The port to listen. |
| </span></span><span style="display:flex;"><span> Default: <span style="color:#2aa198">8091</span> |
| </span></span><span style="display:flex;"><span> --storeMode, -m |
| </span></span><span style="display:flex;"><span> log store mode : file、db |
| </span></span><span style="display:flex;"><span> Default: file |
| </span></span><span style="display:flex;"><span> --help |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>e.g. |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>sh seata-server.sh -p <span style="color:#2aa198">8091</span> -h 127.0.0.1 -m file |
| </span></span></code></pre></div></description></item><item><title>Overview: 通过网关将 http 流量接入 Dubbo 后端服务</title><link>https://dubbo.apache.org/zh-cn/overview/tasks/ecosystem/gateway/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/overview/tasks/ecosystem/gateway/</guid><description> |
| <p>更多网关接入案例,请参考</p> |
| <ul> |
| <li><a href="https://dubbo.apache.org/zh-cn/blog/2022/05/04/%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87-apache-shenyu-%E7%BD%91%E5%85%B3%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1/">使用 Apache Shenyu 代理 Dubbo 流量</a></li> |
| <li><a href="https://dubbo.apache.org/zh-cn/blog/2023/04/01/%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87-higress-%E7%BD%91%E5%85%B3%E4%BB%A3%E7%90%86-dubbo-%E6%9C%8D%E5%8A%A1/">使用 Higress 代理 Dubbo 流量</a></li> |
| </ul> |
| <h2 id="背景">背景</h2> |
| <p><a href="https://dubbo.apache.org/zh-cn/">Apache Dubbo</a> 是由阿里巴巴开源并捐赠给 Apache 的微服务开发框架,它提供了 RPC 通信与微服务治理两大关键能力。不仅经过了阿里电商场景中海量流量的验证,也在国内的技术公司中被广泛落地。</p> |
| <p>在实际应用场景中,Apache Dubbo 一般会作为后端系统间 RPC 调用的实现框架,当需要提供 HTTP 接口给到前端时,会通过一个「胶水层」将 Dubbo Service 包装成 HTTP 接口,再交付到前端系统。</p> |
| <p><a href="https://apisix.apache.org/">Apache APISIX</a> 是 Apache 软件基金会的顶级开源项目,也是当前最活跃的开源网关项目。作为一个动态、实时、高性能的开源 API 网关,Apache APISIX 提供了负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。</p> |
| <p>得益于 Apache Dubbo 的应用场景优势,Apache APISIX 基于开源项目 tengine/mod_dubbo 模块为 Apache Dubbo 服务配备了HTTP 网关能力。通过 dubbo-proxy 插件,可以轻松地将 Dubbo Service 发布为 HTTP 服务。</p> |
| <p><img src="https://dubbo.apache.org/imgs/blog/apisix-plugin/1.png" alt="架构图"></p> |
| <h2 id="如何使用">如何使用</h2> |
| <h3 id="入门篇安装使用">入门篇:安装使用</h3> |
| <blockquote> |
| <p>这里我们建议使用 Apache APISIX 2.11 版本镜像进行安装。该版本的 APISIX-Base 中已默认编译了 Dubbo 模块,可直接使用 <code>dubbo-proxy</code> 插件。</p> |
| </blockquote> |
| <p>在接下来的操作中,我们将使用 <a href="https://github.com/apache/dubbo-samples"><code>dubbo-samples</code></a> 项目进行部分展示。该项目是一些使用 Apache Dubbo 实现的 Demo 应用,本文中我们采用其中的一个子模块作为 Dubbo Provider。</p> |
| <p>在进入正式操作前,我们先简单看下 Dubbo 接口的定义、配置以及相关实现。</p> |
| <h4 id="接口实现一览">接口实现一览</h4> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">DemoService</span> { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#586e75">/** |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * standard samples dubbo infterace demo |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @param context pass http infos |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> * @return Map&lt;String, Object&gt;&lt;/&gt; pass to response http |
| </span></span></span><span style="display:flex;"><span><span style="color:#586e75"> **/</span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">apisixDubbo</span>(Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> httpRequestContext); |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>如上所示,Dubbo 接口的定义是固定的。即方法参数中 <code>Map</code> 表示 APISIX 传递给 Dubbo Provider 关于 HTTP request 的一些信息(如:header、body&hellip;)。而方法返回值的 <code>Map</code> 表示 Dubbo Provider 传递给 APISIX 要如何返回 HTTP response 的一些信息。</p> |
| <p>接口信息配置好之后可通过 XML 配置方式发布 DemoService。</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- service implementation, as same as regular local bean --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;bean</span> id=<span style="color:#2aa198">&#34;demoService&#34;</span> class=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.provider.DemoServiceImpl&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#586e75">&lt;!-- declare the service interface to be exported --&gt;</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">&lt;dubbo:service</span> interface=<span style="color:#2aa198">&#34;org.apache.dubbo.samples.apisix.DemoService&#34;</span> ref=<span style="color:#2aa198">&#34;demoService&#34;</span><span style="color:#268bd2">/&gt;</span> |
| </span></span></code></pre></div><p>通过上述配置后,Consumer 可通过 <code>org.apache.dubbo.samples.apisix.DemoService</code> 访问其中的<code>apisixDubbo</code> 方法。具体接口实现如下:</p> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DemoServiceImpl</span> <span style="color:#268bd2">implements</span> DemoService { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">apisixDubbo</span>(Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> httpRequestContext) { |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (Map.Entry<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> entry : httpRequestContext.entrySet()) { |
| </span></span><span style="display:flex;"><span> System.out.println(<span style="color:#2aa198">&#34;Key = &#34;</span> <span style="color:#719e07">+</span> entry.getKey() <span style="color:#719e07">+</span> <span style="color:#2aa198">&#34;, Value = &#34;</span> <span style="color:#719e07">+</span> entry.getValue()); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> ret <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span>(); |
| </span></span><span style="display:flex;"><span> ret.put(<span style="color:#2aa198">&#34;body&#34;</span>, <span style="color:#2aa198">&#34;dubbo success\n&#34;</span>); <span style="color:#586e75">// http response body</span> |
| </span></span><span style="display:flex;"><span> ret.put(<span style="color:#2aa198">&#34;status&#34;</span>, <span style="color:#2aa198">&#34;200&#34;</span>); <span style="color:#586e75">// http response status</span> |
| </span></span><span style="display:flex;"><span> ret.put(<span style="color:#2aa198">&#34;test&#34;</span>, <span style="color:#2aa198">&#34;123&#34;</span>); <span style="color:#586e75">// http response header</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> ret; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><p>上述代码中,<code>DemoServiceImpl</code> 会打印接收到的 <code>httpRequestContext</code>,并通过返回包含有指定 Key 的 Map 对象去描述该 Dubbo 请求的 HTTP 响应。</p> |
| <h4 id="操作步骤">操作步骤</h4> |
| <ol> |
| <li>启动 <a href="https://github.com/apache/dubbo-samples/tree/master/2-advanced/dubbo-samples-tengine#install-dubbo"><code>dubbo-samples</code></a>。</li> |
| <li>在 <code>config.yaml</code> 文件中进行 <code>dubbo-proxy</code> 插件启用。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#586e75"># Add this in config.yaml</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">plugins</span>: |
| </span></span><span style="display:flex;"><span> - ... <span style="color:#586e75"># plugin you need</span> |
| </span></span><span style="display:flex;"><span> - dubbo-proxy |
| </span></span></code></pre></div><ol start="3"> |
| <li>创建指向 Dubbo Provider 的 Upstream。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H <span style="color:#2aa198">&#39;X-API-KEY: edd1c9f034335f136f87ad84b625c8f1&#39;</span> -X PUT -d <span style="color:#2aa198">&#39; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198">{ |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;nodes&#34;: { |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;127.0.0.1:20880&#34;: 1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> }, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;type&#34;: &#34;roundrobin&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198">}&#39;</span> |
| </span></span></code></pre></div><ol start="4"> |
| <li>为 DemoService 暴露一个 HTTP 路由。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl http://127.0.0.1:9180/apisix/admin/routes/1 -H <span style="color:#2aa198">&#39;X-API-KEY: edd1c9f034335f136f87ad84b625c8f1&#39;</span> -X PUT -d <span style="color:#2aa198">&#39; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198">{ |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;host&#34;: &#34;example.org&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;uris&#34;: [ |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;/demo&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> ], |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;plugins&#34;: { |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;dubbo-proxy&#34;: { |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;service_name&#34;: &#34;org.apache.dubbo.samples.apisix.DemoService&#34;, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;service_version&#34;: &#34;0.0.0&#34;, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;method&#34;: &#34;apisixDubbo&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> } |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> }, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;upstream_id&#34;: 1 |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198">}&#39;</span> |
| </span></span></code></pre></div><ol start="5"> |
| <li>使用 curl 命令请求 Apache APISIX,并查看返回结果。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl http://127.0.0.1:9080/demo -H <span style="color:#2aa198">&#34;Host: example.org&#34;</span> -X POST --data <span style="color:#2aa198">&#39;{&#34;name&#34;: &#34;hello&#34;}&#39;</span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>&lt; HTTP/1.1 <span style="color:#2aa198">200</span> OK |
| </span></span><span style="display:flex;"><span>&lt; Date: Sun, <span style="color:#2aa198">26</span> Dec <span style="color:#2aa198">2021</span> 11:33:27 GMT |
| </span></span><span style="display:flex;"><span>&lt; Content-Type: text/plain; <span style="color:#268bd2">charset</span><span style="color:#719e07">=</span>utf-8 |
| </span></span><span style="display:flex;"><span>&lt; Content-Length: <span style="color:#2aa198">14</span> |
| </span></span><span style="display:flex;"><span>&lt; Connection: keep-alive |
| </span></span><span style="display:flex;"><span>&lt; test: <span style="color:#2aa198">123</span> |
| </span></span><span style="display:flex;"><span>&lt; Server: APISIX/2.11.0 |
| </span></span><span style="display:flex;"><span>&lt; |
| </span></span><span style="display:flex;"><span>dubbo success |
| </span></span></code></pre></div><p>:::note 说明 |
| 上述代码返回中包含了 <code>test: 123</code> Header,以及 <code>dubbo success</code> 字符串作为 Body 体。这与我们在 <code>DemoServiceImpl</code> 编码的预期效果一致。 |
| :::</p> |
| <ol start="6"> |
| <li>查看 Dubbo Provider 的日志。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>Key = content-length, Value = 17 |
| </span></span><span style="display:flex;"><span>Key = host, Value = example.org |
| </span></span><span style="display:flex;"><span>Key = content-type, Value = application/x-www-form-urlencoded |
| </span></span><span style="display:flex;"><span>Key = body, Value = [B@70754265 |
| </span></span><span style="display:flex;"><span>Key = accept, Value = */* |
| </span></span><span style="display:flex;"><span>Key = user-agent, Value = curl/7.80.0 |
| </span></span></code></pre></div><p>:::note 说明 |
| 通过 <code>httpRequestContext</code> 可以拿到 HTTP 请求的 Header 和 Body。其中 Header 会作为 Map 元素,而 Body 中 Key 值是固定的字符串&quot;body&quot;,Value 则代表 Byte 数组。 |
| :::</p> |
| <h3 id="进阶篇复杂场景示例">进阶篇:复杂场景示例</h3> |
| <p>在上述的简单用例中可以看出,我们确实通过 Apache APISIX 将 Dubbo Service 发布为一个 HTTP 服务,但是在使用过程中的限制也非常明显。比如:接口的参数和返回值都必须要是 <code>Map&lt;String, Object&gt;</code>。</p> |
| <p>那么,如果项目中出现已经定义好、但又不符合上述限制的接口,该如何通过 Apache APISIX 来暴露 HTTP 服务呢?</p> |
| <h4 id="操作步骤-1">操作步骤</h4> |
| <p>针对上述场景,我们可以通过 HTTP Request Body 描述要调用的 Service 和 Method 以及对应参数,再利用 Java 的反射机制实现目标方法的调用。最后将返回值序列化为 JSON,并写入到 HTTP Response Body 中。</p> |
| <p>这样就可以将 Apache APISIX 的 「HTTP to Dubbo」 能力进一步加强,并应用到所有已存在的 Dubbo Service 中。具体操作可参考下方:</p> |
| <ol> |
| <li>为已有项目增加一个 Dubbo Service 用来统一处理 HTTP to Dubbo 的转化。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DubboInvocationParameter</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String type; |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String value; |
| </span></span><span style="display:flex;"><span>} |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">DubboInvocation</span> { |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String service; |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> String method; |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> DubboInvocationParameter<span style="color:#719e07">[]</span> parameters; |
| </span></span><span style="display:flex;"><span>} |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">interface</span> <span style="color:#268bd2">HTTP2DubboService</span> { |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">invoke</span>(Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> context) <span style="color:#268bd2">throws</span> Exception; |
| </span></span><span style="display:flex;"><span>} |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">@Component</span> |
| </span></span><span style="display:flex;"><span><span style="color:#268bd2">public</span> <span style="color:#268bd2">class</span> <span style="color:#268bd2">HTTP2DubboServiceImpl</span> <span style="color:#268bd2">implements</span> HTTP2DubboService { |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Autowired</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">private</span> ApplicationContext appContext; |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">@Override</span> |
| </span></span><span style="display:flex;"><span> <span style="color:#268bd2">public</span> Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> <span style="color:#268bd2">invoke</span>(Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> context) <span style="color:#268bd2">throws</span> Exception { |
| </span></span><span style="display:flex;"><span> DubboInvocation invocation <span style="color:#719e07">=</span> JSONObject.parseObject((<span style="color:#dc322f">byte</span><span style="color:#719e07">[]</span>) context.get(<span style="color:#2aa198">&#34;body&#34;</span>), DubboInvocation.class); |
| </span></span><span style="display:flex;"><span> Object<span style="color:#719e07">[]</span> args <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> Object<span style="color:#719e07">[</span>invocation.getParameters().size()<span style="color:#719e07">]</span>; |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">for</span> (<span style="color:#dc322f">int</span> i <span style="color:#719e07">=</span> 0; i <span style="color:#719e07">&lt;</span> args.length; i<span style="color:#719e07">++</span>) { |
| </span></span><span style="display:flex;"><span> DubboInvocationParameter parameter <span style="color:#719e07">=</span> invocation.getParameters().get(i); |
| </span></span><span style="display:flex;"><span> args<span style="color:#719e07">[</span>i<span style="color:#719e07">]</span> <span style="color:#719e07">=</span> JSONObject.parseObject(parameter.getValue(), Class.forName(parameter.getType())); |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span> Object svc <span style="color:#719e07">=</span> appContext.getBean(Class.forName(invocation.getService())); |
| </span></span><span style="display:flex;"><span> Object result <span style="color:#719e07">=</span> svc.getClass().getMethod(invocation.getMethod()).invoke(args); |
| </span></span><span style="display:flex;"><span> Map<span style="color:#719e07">&lt;</span>String, Object<span style="color:#719e07">&gt;</span> httpResponse <span style="color:#719e07">=</span> <span style="color:#719e07">new</span> HashMap<span style="color:#719e07">&lt;&gt;</span>(); |
| </span></span><span style="display:flex;"><span> httpResponse.put(<span style="color:#2aa198">&#34;status&#34;</span>, 200); |
| </span></span><span style="display:flex;"><span> httpResponse.put(<span style="color:#2aa198">&#34;body&#34;</span>, JSONObject.toJSONString(result)); |
| </span></span><span style="display:flex;"><span> <span style="color:#719e07">return</span> httpResponse; |
| </span></span><span style="display:flex;"><span> } |
| </span></span><span style="display:flex;"><span> |
| </span></span><span style="display:flex;"><span>} |
| </span></span></code></pre></div><ol start="2"> |
| <li>通过如下命令请求来发起相关调用。</li> |
| </ol> |
| <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl http://127.0.0.1:9080/demo -H <span style="color:#2aa198">&#34;Host: example.org&#34;</span> -X POST --data <span style="color:#2aa198">&#39; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198">{ |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;service&#34;: &#34;org.apache.dubbo.samples.apisix.DemoService&#34;, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;method&#34;: &#34;createUser&#34;, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;parameters&#34;: [ |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> { |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;type&#34;: &#34;org.apache.dubbo.samples.apisix.User&#34;, |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> &#34;value&#34;: &#34;{&#39;</span>name<span style="color:#2aa198">&#39;: &#39;</span>hello<span style="color:#2aa198">&#39;}&#34; |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> } |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198"> ] |
| </span></span></span><span style="display:flex;"><span><span style="color:#2aa198">}&#39;</span> |
| </span></span></code></pre></div><h2 id="总结">总结</h2> |
| <p>本文为大家介绍了如何借助 Apache APISIX 实现 Dubbo Service 的代理,通过引入 <code>dubbo-proxy</code> 插件便可为 Dubbo 框架的后端系统构建更简单更高效的流量链路。</p> |
| <p>希望通过上述操作步骤和用例场景分享,能为大家在相关场景的使用提供借鉴思路。更多关于 <code>dubbo-proxy</code> 插件的介绍与使用可参考<a href="https://apisix.apache.org/docs/apisix/plugins/dubbo-proxy/">官方文档</a>。</p></description></item><item><title>Overview: 配置中心</title><link>https://dubbo.apache.org/zh-cn/overview/tasks/ecosystem/configuration/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/overview/tasks/ecosystem/configuration/</guid><description/></item><item><title>Overview: 元数据中心</title><link>https://dubbo.apache.org/zh-cn/overview/tasks/ecosystem/metadata-center/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/overview/tasks/ecosystem/metadata-center/</guid><description/></item><item><title>Overview: 注册中心</title><link>https://dubbo.apache.org/zh-cn/overview/tasks/ecosystem/registry/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dubbo.apache.org/zh-cn/overview/tasks/ecosystem/registry/</guid><description/></item></channel></rss> |