在一个服务化的系统中,有时候我们会在同一个应用中对外提供多个服务,例如,我们的商品详情应用既提供价格查询也提供库存查询功能,如下
下面程序模拟用户不停的访问两个服务,而库存服务有 50% 的几率会超时(程序中用 sleep 1小时模拟)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
| import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger;
public class GoodsDetailService { private ExecutorService threadPool = Executors.newFixedThreadPool(10); private AtomicInteger badCount = new AtomicInteger();
public int getStockCount(String skuId) {
Future<Integer> future = threadPool.submit(() -> { System.out.println("开始查询 " + skuId + " 的库存信息"); long waitMills = 20; if (Math.random() > 0.5) { System.out.println("---->要坏了,这是第 " + badCount.incrementAndGet() + " 个了"); waitMills = 600000; } try { Thread.sleep(waitMills); } catch (InterruptedException e) { } System.out.println("结束查询 " + skuId + " 的库存信息"); return 100; });
try { return future.get(); } catch (Exception e) { e.printStackTrace(); } return 0; }
public Double getPrice(String skuId) { Future<Double> future = threadPool.submit(() -> { System.out.println("开始查询 " + skuId + " 的价格信息"); try { Thread.sleep(30); } catch (InterruptedException e) { } System.out.println("结束查询 " + skuId + " 的价格信息"); return 99.99d; }); try { return future.get(); } catch (Exception e) { e.printStackTrace(); } return 0d; }
public static void main(String[] args) { GoodsDetailService goodsDetailService = new GoodsDetailService(); ExecutorService executorService = Executors.newFixedThreadPool(30); int i = 0; while (true) { String skuId = String.format("sku-%03d", ++i); System.out.println("第 " + i + " 个用户来查询 " + skuId + " 的信息了"); executorService.execute(() -> goodsDetailService.getStockCount(skuId)); executorService.execute(() -> goodsDetailService.getPrice(skuId)); try { Thread.sleep(500); } catch (InterruptedException e) { } } } } ```
程序运行结果
```console ... 开始查询 sku-023 的价格信息 结束查询 sku-023 的价格信息 第 24 个用户来查询 sku-024 的信息了 开始查询 sku-024 的库存信息 结束查询 sku-024 的库存信息 开始查询 sku-024 的价格信息 结束查询 sku-024 的价格信息 第 25 个用户来查询 sku-025 的信息了 开始查询 sku-025 的库存信息 ---->要坏了,这是第 10 个了 第 26 个用户来查询 sku-026 的信息了 第 27 个用户来查询 sku-027 的信息了 第 28 个用户来查询 sku-028 的信息了 ```
从结果可以看出,在第 25 个用户访问时,库存服务已有 10 次 超时,占完了所有线程,价格服务一些正常,却因没有线程可用导致不可访问,下面我们对程序进行优化,将两个服务的线程池隔离开
![](https:
主要对原代码做如下修改:
1. 原线程池 `private ExecutorService threadPool = Executors.newFixedThreadPool(10);` 改成两个 `private ExecutorService stockPool = Executors.newFixedThreadPool(5);` 和 `private ExecutorService pricePool = Executors.newFixedThreadPool(5);` 2. 两个服务分别使用自己的线程池
```java public int getStockCount(String skuId) { Future<Integer> future = stockPool.submit(() -> { ... return 0; } ```
```java public Double getPrice(String skuId) { Future<Double> future = pricePool.submit(() -> { ... return 0d; } ```
优化完再次运行程序,通过程序结果可以看出,在第 14 个用户访问时,库存服务已有 5 次 超时,其线程已消耗殆尽,此时用户已经不能正常访问库存服务了,令人欣喜的是,得益于我们已经对服务资源做了隔离,价格服务并没有受到影响,仍然可以正常提供服务。
```console ... 第 14 个用户来查询 sku-014 的信息了 开始查询 sku-014 的库存信息 ---->要坏了,这是第 5 个了 开始查询 sku-014 的价格信息 结束查询 sku-014 的价格信息 第 15 个用户来查询 sku-015 的信息了 开始查询 sku-015 的价格信息 结束查询 sku-015 的价格信息 第 16 个用户来查询 sku-016 的信息了 开始查询 sku-016 的价格信息 结束查询 sku-016 的价格信息 ...
|