最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Boot高效數(shù)據(jù)聚合之道深入講解

 更新時間:2019年06月12日 10:31:41   作者:lvyahui  
這篇文章主要給大家介紹了關(guān)于Spring Boot高效數(shù)據(jù)聚合之道的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Spring Boot具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧

背景

接口開發(fā)是后端開發(fā)中最常見的場景, 可能是RESTFul接口, 也可能是RPC接口. 接口開發(fā)往往是從各處撈出數(shù)據(jù), 然后組裝成結(jié)果, 特別是那些偏業(yè)務(wù)的接口.

例如, 我現(xiàn)在需要實(shí)現(xiàn)一個接口, 拉取用戶基礎(chǔ)信息+用戶的博客列表+用戶的粉絲數(shù)據(jù)的整合數(shù)據(jù), 假設(shè)已經(jīng)有如下三個接口可以使用, 分別用來獲取 用戶基礎(chǔ)信息 ,用戶博客列表, 用戶的粉絲數(shù)據(jù).

用戶基礎(chǔ)信息

@Service
public class UserServiceImpl implements UserService {
 @Override
 public User get(Long id) {
 try {Thread.sleep(1000L);} catch (InterruptedException e) {}
 /* mock a user*/
 User user = new User();
 user.setId(id);
 user.setEmail("lvyahui8@gmail.com");
 user.setUsername("lvyahui8");
 return user;
 }
}

用戶博客列表

@Service
public class PostServiceImpl implements PostService {
 @Override
 public List<Post> getPosts(Long userId) {
 try { Thread.sleep(1000L); } catch (InterruptedException e) {}
 Post post = new Post();
 post.setTitle("spring data aggregate example");
 post.setContent("No active profile set, falling back to default profiles");
 return Collections.singletonList(post);
 }
}

用戶的粉絲數(shù)據(jù)

@Service
public class FollowServiceImpl implements FollowService {
 @Override
 public List<User> getFollowers(Long userId) {
 try { Thread.sleep(1000L); } catch (InterruptedException e) {}
 int size = 10;
 List<User> users = new ArrayList<>(size);
 for(int i = 0 ; i < size; i++) {
  User user = new User();
  user.setUsername("name"+i);
  user.setEmail("email"+i+"@fox.com");
  user.setId((long) i);
  users.add(user);
 };
 return users;
 }
}

注意, 每一個方法都sleep了1s以模擬業(yè)務(wù)耗時.

我們需要再封裝一個接口, 來拼裝以上三個接口的數(shù)據(jù).

PS: 這樣的場景實(shí)際在工作中很常見, 而且往往我們需要拼湊的數(shù)據(jù), 是要走網(wǎng)絡(luò)請求調(diào)到第三方去的. 另外可能有人會想, 為何不分成3個請求? 實(shí)際為了客戶端網(wǎng)絡(luò)性能考慮, 往往會在一次網(wǎng)絡(luò)請求中, 盡可能多的傳輸數(shù)據(jù), 當(dāng)然前提是這個數(shù)據(jù)不能太大, 否則傳輸?shù)暮臅r會影響渲染. 許多APP的首頁, 看著復(fù)雜, 實(shí)際也只有一個接口, 一次性拉下所有數(shù)據(jù), 客戶端開發(fā)也簡單.

串行實(shí)現(xiàn)

編寫性能優(yōu)良的接口不僅是每一位后端程序員的技術(shù)追求, 也是業(yè)務(wù)的基本訴求. 一般情況下, 為了保證更好的性能, 往往需要編寫更復(fù)雜的代碼實(shí)現(xiàn).

但凡人皆有惰性, 因此, 往往我們會像下面這樣編寫串行調(diào)用的代碼

@Component
public class UserQueryFacade {
 @Autowired
 private FollowService followService;
 @Autowired
 private PostService postService;
 @Autowired
 private UserService userService;
 
 public User getUserData(Long userId) {
  User user = userService.get(userId);
  user.setPosts(postService.getPosts(userId));
  user.setFollowers(followService.getFollowers(userId));
  return user;
 }
}

很明顯, 上面的代碼, 效率低下, 起碼要3s才能拿到結(jié)果, 且一旦用到某個接口的數(shù)據(jù), 便需要注入相應(yīng)的service, 復(fù)用麻煩.

并行實(shí)現(xiàn)

有追求的程序員可能立馬會考慮到, 這幾項(xiàng)數(shù)據(jù)之間并無強(qiáng)依賴性, 完全可以并行獲取嘛, 通過異步線程+CountDownLatch+Future實(shí)現(xiàn), 就像下面這樣.

@Component
public class UserQueryFacade {
 @Autowired
 private FollowService followService;
 @Autowired
 private PostService postService;
 @Autowired
 private UserService userService;
 
 public User getUserDataByParallel(Long userId) throws InterruptedException, ExecutionException {
  ExecutorService executorService = Executors.newFixedThreadPool(3);
  CountDownLatch countDownLatch = new CountDownLatch(3);
  Future<User> userFuture = executorService.submit(() -> {
   try{
    return userService.get(userId);
   }finally {
    countDownLatch.countDown();
   }
  });
  Future<List<Post>> postsFuture = executorService.submit(() -> {
   try{
    return postService.getPosts(userId);
   }finally {
    countDownLatch.countDown();
   }
  });
  Future<List<User>> followersFuture = executorService.submit(() -> {
   try{
    return followService.getFollowers(userId);
   }finally {
    countDownLatch.countDown();
   }
  });
  countDownLatch.await();
  User user = userFuture.get();
  user.setFollowers(followersFuture.get());
  user.setPosts(postsFuture.get());
  return user;
 }
}

上面的代碼, 將串行調(diào)用改為并行調(diào)用, 在有限并發(fā)級別下, 能極大提高性能. 但很明顯, 它過于復(fù)雜, 如果每個接口都為了并行執(zhí)行都寫這樣一段代碼, 簡直是噩夢.

優(yōu)雅的注解實(shí)現(xiàn)

熟悉java的都知道, java有一種非常便利的特性 ~~ 注解. 簡直是黑魔法. 往往只需要給類或者方法上添加一些注解, 便可以實(shí)現(xiàn)非常復(fù)雜的功能.

有了注解, 再結(jié)合Spring依賴自動注入的思想, 那么我們可不可以通過注解的方式, 自動注入依賴, 自動并行調(diào)用接口呢? 答案是肯定的.

首先, 我們先定義一個聚合接口

@Component
public class UserAggregate {
 @DataProvider(id="userFullData")
 public User userFullData(@DataConsumer(id = "user") User user,
        @DataConsumer(id = "posts") List<Post> posts,
        @DataConsumer(id = "followers") List<User> followers) {
  user.setFollowers(followers);
  user.setPosts(posts);
  return user;
 }
}

其中

  • @DataProvider 表示這個方法是一個數(shù)據(jù)提供者, 數(shù)據(jù)Id為 userFullData
  • @DataConsumer 表示這個方法的參數(shù), 需要消費(fèi)數(shù)據(jù), 數(shù)據(jù)Id為 user ,posts, followers.

當(dāng)然, 原來的3個原子服務(wù) 用戶基礎(chǔ)信息 ,用戶博客列表, 用戶的粉絲數(shù)據(jù), 也分別需要添加一些注解

@Service
public class UserServiceImpl implements UserService {
 @DataProvider(id = "user")
 @Override
 public User get(@InvokeParameter("userId") Long id) {
@Service
public class PostServiceImpl implements PostService {
 @DataProvider(id = "posts")
 @Override
 public List<Post> getPosts(@InvokeParameter("userId") Long userId) {
@Service
public class FollowServiceImpl implements FollowService {
 @DataProvider(id = "followers")
 @Override
 public List<User> getFollowers(@InvokeParameter("userId") Long userId) {

其中

  • @DataProvider 與前面的含義相同, 表示這個方法是一個數(shù)據(jù)提供者
  • @InvokeParameter 表示方法執(zhí)行時, 需要手動傳入的參數(shù)

這里注意 @InvokeParameter 和 @DataConsumer的區(qū)別, 前者需要用戶在最上層調(diào)用時手動傳參; 而后者, 是由框架自動分析依賴, 并異步調(diào)用取得結(jié)果之后注入的.

最后, 僅僅只需要調(diào)用一個統(tǒng)一的門面(Facade)接口, 傳遞數(shù)據(jù)Id, Invoke Parameters,以及返回值類型. 剩下的并行處理, 依賴分析和注入, 完全由框架自動處理.

@Component
public class UserQueryFacade {
 @Autowired
 private DataBeanAggregateQueryFacade dataBeanAggregateQueryFacade;

 public User getUserFinal(Long userId) throws InterruptedException, 
    IllegalAccessException, InvocationTargetException {
  return dataBeanAggregateQueryFacade.get("userFullData",
    Collections.singletonMap("userId", userId), User.class);
 }
}

如何用在你的項(xiàng)目中

上面的功能, 筆者已經(jīng)封裝為一個spring boot starter, 并發(fā)布到maven中央倉庫.

只需在你的項(xiàng)目引入依賴.

<dependency>
 <groupId>io.github.lvyahui8</groupId>
 <artifactId>spring-boot-data-aggregator-example</artifactId>
 <version>1.0.1</version>
</dependency>

并在 application.properties 文件中聲明注解的掃描路徑.

# 替換成你需要掃描注解的包
io.github.lvyahui8.spring.base-packages=io.github.lvyahui8.spring.example

之后, 就可以使用如下注解和 Spring Bean 實(shí)現(xiàn)聚合查詢

  • @DataProvider
  • @DataConsumer
  • @InvokeParameter
  • Spring Bean DataBeanAggregateQueryFacade

注意, @DataConsumer 和 @InvokeParameter 可以混合使用, 可以用在同一個方法的不同參數(shù)上. 且方法的所有參數(shù)必須有其中一個注解, 不能有沒有注解的參數(shù).

項(xiàng)目地址和上述示例代碼: https://github.com/lvyahui8/spring-boot-data-aggregator

后期計(jì)劃

后續(xù)筆者將繼續(xù)完善異常處理, 超時邏輯, 解決命名沖突的問題, 并進(jìn)一步提高插件的易用性, 高可用性, 擴(kuò)展性

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。

相關(guān)文章

  • 在maven工程里運(yùn)行java main方法

    在maven工程里運(yùn)行java main方法

    這篇文章主要介紹了在maven工程里運(yùn)行java main方法,需要的朋友可以參考下
    2014-04-04
  • Eclipse下基于Java的OpenCV開發(fā)環(huán)境配置教程

    Eclipse下基于Java的OpenCV開發(fā)環(huán)境配置教程

    這篇文章主要為大家詳細(xì)介紹了Eclipse下基于Java的OpenCV開發(fā)環(huán)境配置教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • Java volatile關(guān)鍵字特性講解上篇

    Java volatile關(guān)鍵字特性講解上篇

    JMM要求保證可見性、原子性、有序性,volatile可以保證其中的兩個,本篇文章具體驗(yàn)證volatile的可見性,不原子性和禁重排,同時解決volatile的不保證原子性,讓代碼具有原子性
    2022-12-12
  • Java檢測網(wǎng)絡(luò)是否正常通訊

    Java檢測網(wǎng)絡(luò)是否正常通訊

    在網(wǎng)絡(luò)應(yīng)用程序中,檢測IP地址和端口是否通常是必要的,本文主要介紹了Java檢測網(wǎng)絡(luò)是否正常通訊,具有一定的參考價值,感興趣的可以了解一下
    2023-11-11
  • Spring Boot項(xiàng)目中定制攔截器的方法詳解

    Spring Boot項(xiàng)目中定制攔截器的方法詳解

    這篇文章主要介紹了Spring Boot項(xiàng)目中定制攔截器的方法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-10-10
  • springboot整合redisson實(shí)現(xiàn)延時隊(duì)列(附倉庫地址)

    springboot整合redisson實(shí)現(xiàn)延時隊(duì)列(附倉庫地址)

    延時隊(duì)列用于管理需要定時執(zhí)行的任務(wù),對于大數(shù)據(jù)量和高實(shí)時性需求,使用延時隊(duì)列比定時掃庫更高效,Redisson提供一種高效的延時隊(duì)列實(shí)現(xiàn)方式,本文就來詳細(xì)的介紹一下,感興趣都可以了解學(xué)習(xí)
    2024-10-10
  • 使用springboot 打包插件去除jar包瘦身

    使用springboot 打包插件去除jar包瘦身

    這篇文章主要介紹了使用springboot 打包插件去除jar包瘦身的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java實(shí)現(xiàn)分頁代碼

    Java實(shí)現(xiàn)分頁代碼

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)分頁代碼,提高查詢效率,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-09-09
  • Java日期操作方法工具類實(shí)例【包含日期比較大小,相加減,判斷,驗(yàn)證,獲取年份等】

    Java日期操作方法工具類實(shí)例【包含日期比較大小,相加減,判斷,驗(yàn)證,獲取年份等】

    這篇文章主要介紹了Java日期操作方法工具類,結(jié)合完整實(shí)例形式分析了java針對日期的各種常見操作,包括日期比較大小,相加減,判斷,驗(yàn)證,獲取年份、天數(shù)、星期等,需要的朋友可以參考下
    2017-11-11
  • JavaWeb三大組件之Filter過濾器詳解

    JavaWeb三大組件之Filter過濾器詳解

    這篇文章主要介紹了JavaWeb三大組件之Filter過濾器詳解,過濾器Filter是Java?Web應(yīng)用中的一種組件,它在請求到達(dá)Servlet或JSP之前或者響應(yīng)送回客戶端之前,對請求和響應(yīng)進(jìn)行預(yù)處理和后處理操作,需要的朋友可以參考下
    2023-10-10

最新評論

平原县| 都安| 通化市| 辛集市| 乌兰浩特市| 张家川| 南康市| 独山县| 彰武县| 吉林市| 双城市| 西乌珠穆沁旗| 周宁县| 崇仁县| 彭泽县| 中江县| 西乡县| 兴海县| 乌鲁木齐县| 三都| 伊通| 定西市| 夏河县| 罗定市| 松溪县| 台北市| 莱西市| 加查县| 永顺县| 通化县| 济源市| 彩票| 白玉县| 德安县| 博白县| 达拉特旗| 天津市| 禹州市| 金平| 济南市| 重庆市|