欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringCloud解決Feign異步回調(diào)問題(SpringBoot+Async+Future實(shí)現(xiàn))

 更新時(shí)間:2022年11月23日 11:17:03   作者:弱水提滄  
這篇文章主要介紹了SpringCloud解決Feign異步回調(diào)問題(SpringBoot+Async+Future實(shí)現(xiàn)),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

近期,需要對之前的接口進(jìn)行優(yōu)化,縮短接口的響應(yīng)時(shí)間,但是springcloud中的feign是不支持傳遞異步化的回調(diào)結(jié)果的,因此有了以下的解決方案,記錄一下,僅供參考。

一、背景

對于一個(gè)頁面上的所有實(shí)例有一個(gè)查詢權(quán)限的接口,同時(shí)有四個(gè)操作類型的需要查詢且接口僅支持單個(gè)實(shí)例單個(gè)操作類型操作。

簡單來說,假設(shè)實(shí)例查詢退訂權(quán)限需要1秒鐘,那么四個(gè)操作共需4秒鐘,一共20個(gè)實(shí)例的話,那么實(shí)際所需時(shí)間為80秒。

這樣顯然是不符合要求的。經(jīng)過本文的優(yōu)化后,可以達(dá)到20秒。

二、設(shè)計(jì)方案

需要指出,由于各種限制原因,無法直接在工程中進(jìn)行rest接口的調(diào)用,否則是直接可以@Async異步調(diào)用的。 

(一)使用框架

產(chǎn)品工程使用SpringCloud微服務(wù),同時(shí)通過Feign調(diào)用權(quán)限的微服務(wù),服務(wù)都是使用SpringBoot實(shí)現(xiàn)。

(二)實(shí)現(xiàn)方案

由于Feign的特殊性,目前并不支持異步返回Future,直接通過Future是會(huì)報(bào)錯(cuò)的。

因此可以在權(quán)限微服務(wù)中異步執(zhí)行查詢操作權(quán)限,同時(shí)再異步起一個(gè)線程去等待所有的異步結(jié)果,這一個(gè)線程是阻塞的,同時(shí)由阻塞得到的最終結(jié)果,封裝成自己想要的返回體,返回給調(diào)用方即可,這樣相當(dāng)于大致只需要等待一個(gè)實(shí)例的操作時(shí)間即可。

三、示例代碼

項(xiàng)目代碼我就不放上來了,我將寫一個(gè)Demo作為示例,基本的思想都在里面了。

(一)被調(diào)用方

1. 配置異步化及異步線程池 - AsyncConfig類

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
 
    @Bean("taskPoolExecutor")
    public Executor taskPoolExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(20);
        executor.setMaxPoolSize(30);
        executor.setQueueCapacity(200);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("taskPoolExecutor-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
 
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new SimpleAsyncUncaughtExceptionHandler();
    }
}

2. Controller層 

@RestController
@Slf4j
public class AsyncController {
 
    @Autowired
    private AsyncService asyncService;
 
    /**
     * 異步測試方法
     * @param ids
     * @return
     * @throws Exception
     */
    @GetMapping("/future")
    public List<User> testFuture(int[] ids) throws Exception {
        List<Future<User>> futures = new ArrayList<>();
        futures.add(asyncService.testFuture(ids[0])); //異步方法 異步執(zhí)行四個(gè)用戶的查詢操作
        futures.add(asyncService.testFuture(ids[1]));
        futures.add(asyncService.testFuture(ids[2]));
        futures.add(asyncService.testFuture(ids[3]));
        return asyncService.getReturnValueMange(futures); 異步方法 異步獲取所有的異步回調(diào)值
    }
 
    /**
     * 同步測試方法
     * @param i
     * @return
     * @throws InterruptedException
     */
    @GetMapping("/syncTest")
    public User testsyncTest( Integer i) throws InterruptedException {
        User user = asyncService.testSync(i);
        return user;
    }
}

這里需要解釋說明下,使用一個(gè)list作為future結(jié)果的集合,同時(shí)采用getReturnValueMange(future)方法來獲取所有的異步執(zhí)行結(jié)果,具體可以看下面Service層的代碼邏輯。

3. AsyncService層

@Slf4j
@Service
public class AsyncService {
 
    @Autowired
    private AsyncUnit asyncUnit; //異步實(shí)現(xiàn)util
 
    public Future<User> testFuture(int i) throws InterruptedException {
        log.info("start...");
        long currentTime = System.currentTimeMillis();
        Future<User> user = asyncUnit.testUser(i);
        log.info("done...{}", String.valueOf(System.currentTimeMillis()- currentTime));
        return user;
    }
 
    //獲取異步方法結(jié)果
    public List<User> getReturnValueMange(List<Future<User>> futures) throws Exception {
        Future<List<User>> userFuture = asyncUnit.getReturnValueMange(futures);
        return userFuture.get(); //get()方法為阻塞方法,等待異步返回值
    }
 
    //同步方法-作為比較方法
    public User testSync(int i) throws InterruptedException {
        log.info("start...");
        Thread.sleep(2000);
        return new User(i);
    }

4. AsyncUtil 層

@Service
@Slf4j
public class AsyncUnit {
 
    //異步方法,通過sleep 2秒鐘模擬
    @Async("taskPoolExecutor")
    public Future<User> testUser(int age){
        log.info("異步執(zhí)行start...{}",Thread.currentThread().getName());
        long currentTime = System.currentTimeMillis();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("異步執(zhí)行done...{}, {}",Thread.currentThread().getName(), String.valueOf(System.currentTimeMillis()- currentTime));
        return new AsyncResult<>(new User(age));
    }
 
    @Async("taskPoolExecutor")
    public Future<List<User>> getReturnValueMange(List<Future<User>> futures) throws Exception{
        log.info("異步執(zhí)行start..getReturnValueMange.{}",Thread.currentThread().getName());
        long currentTime = System.currentTimeMillis();
        List<User> users = new ArrayList<>();
        for (Future<User> future : futures) {
            users.add(future.get());
        }
        log.info(""+users.size());
        log.info("異步執(zhí)行done..getReturnValueMange.{}, {}",Thread.currentThread().getName(), String.valueOf(System.currentTimeMillis()- currentTime));
        return new AsyncResult<>(users);
    }
}

(二)調(diào)用方

1. Config 層

@Configuration
public class FeignConfig {
 
    @Bean
    public RequestInterceptor requestInterceptor() {
        return new FeignRequestInterceptor();
    }
}

2.  Controller層

@RestController
@Slf4j
public class AsyncController {
 
    @Autowired
    private RemoteClient remoteClient;
 
    //異步測試方法,查詢五次 模擬所需時(shí)間
    @GetMapping("/future")
    public List<Integer> testFuture() throws InterruptedException, ExecutionException {
        String res = "";
        log.info(" asynccontroller...start...");
        long currentTime = System.currentTimeMillis();
        int[] ids = new int[]{1,2,3,4,5};
        List<User> users = remoteClient.testFuture(ids);
        List<Integer> resList = new ArrayList<>();
        resList.add(users.get(1).getAge());
        log.info(" asynccontroller...done...{}", String.valueOf(System.currentTimeMillis()- currentTime));
        return  resList;
    }
 
    //同步測試方法,通過執(zhí)行5次同步方法模擬查詢
    @GetMapping("/syncTest")
    public String testsyncTest() {
        long currentTime = System.currentTimeMillis();
        List<Integer> ages = new ArrayList<>();
        for(int i=0; i<5; i++){
            User user = remoteClient.testsyncTest(i);
            ages.add(user.getAge());
        }
        log.info("done...{}", String.valueOf(System.currentTimeMillis()- currentTime));
        return  ages.get(0)+ages.get(1)+ages.get(2)+ages.get(3)+ages.get(4)+"";
    }
}

3. FeignClient (RemoteClient)層

@FeignClient(name = "ASYNC")
public interface RemoteClient {
 
 
    @GetMapping("/future")
    List<User> testFuture(@RequestParam("ids") int[] ids);
 
 
    @GetMapping("/futureValue")
    List<String> getReturnValueMange(@RequestBody List<Future<User>> futures);
 
    @GetMapping("/syncTest")
    User testsyncTest(@RequestParam("i") Integer i);
 
}

被調(diào)用方的服務(wù)名為ASYNC,通過Feign調(diào)用,F(xiàn)eign的詳細(xì)說明就不細(xì)說,具體可以自行查詢。

(三)公共類 User

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
 
    private String name;
    private String id;
    private int age;
    private String school;
 
    public User (int age){
        this.age = age;
    }
 
}

四、實(shí)現(xiàn)效果測試

調(diào)用方端口8305 被調(diào)用方8036 eureka端口 8761,將服務(wù)啟動(dòng)如圖所示。

1. 使用Postman進(jìn)行接口測試,首先是同步方法的執(zhí)行。

控制臺(tái)日志

2. 使用Postman進(jìn)行異步方法調(diào)用

控制臺(tái)日志

 可以看到異步執(zhí)行之后,查詢結(jié)果由10秒縮短到2秒,這個(gè)優(yōu)化的時(shí)間為實(shí)例的個(gè)數(shù)為倍數(shù)作為縮短。

五、總結(jié)

本文的優(yōu)化方法基于Feign無法異步返回調(diào)用值得情況下采取的折中方法,如果遇到不在意返回值的異步返回可以直接進(jìn)行異步執(zhí)行,這樣的話可以在毫秒級就執(zhí)行結(jié)束。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 使用SpringAOP獲取用戶操作日志入庫

    使用SpringAOP獲取用戶操作日志入庫

    這篇文章主要介紹了使用SpringAOP獲取用戶操作日志入庫,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 單點(diǎn)登錄的概念及SpringBoot實(shí)現(xiàn)單點(diǎn)登錄的操作方法

    單點(diǎn)登錄的概念及SpringBoot實(shí)現(xiàn)單點(diǎn)登錄的操作方法

    在本文中,我們將使用Spring Boot構(gòu)建一個(gè)基本的單點(diǎn)登錄系統(tǒng),我們將介紹如何使用Spring Security和JSON Web Tokens(JWTs)來實(shí)現(xiàn)單點(diǎn)登錄功能,本文假設(shè)您已經(jīng)熟悉Spring Boot和Spring Security,感興趣的朋友一起看看吧
    2024-10-10
  • Java ArrayList的不同排序方法

    Java ArrayList的不同排序方法

    這篇文章主要介紹了Java ArrayList的不同排序方法,感興趣的小伙伴們可以參考一下
    2015-12-12
  • 分布式調(diào)度XXL-Job整合Springboot2.X實(shí)戰(zhàn)操作過程(推薦)

    分布式調(diào)度XXL-Job整合Springboot2.X實(shí)戰(zhàn)操作過程(推薦)

    這篇文章主要介紹了分布式調(diào)度XXL-Job整合Springboot2.X實(shí)戰(zhàn)操作,包括定時(shí)任務(wù)的使用場景和常見的定時(shí)任務(wù),通過本文學(xué)習(xí)幫助大家該選擇哪個(gè)分布式任務(wù)調(diào)度平臺(tái),對此文感興趣的朋友一起看看吧
    2022-04-04
  • JDBC連接MySQL數(shù)據(jù)庫批量插入數(shù)據(jù)過程詳解

    JDBC連接MySQL數(shù)據(jù)庫批量插入數(shù)據(jù)過程詳解

    這篇文章主要介紹了JDBC連接MySQL數(shù)據(jù)庫批量插入數(shù)據(jù)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • MyBatis插入Insert、InsertSelective的區(qū)別及使用心得

    MyBatis插入Insert、InsertSelective的區(qū)別及使用心得

    這篇文章主要介紹了MyBatis插入Insert、InsertSelective的區(qū)別及使用心得,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java JUC中操作List安全類的集合案例

    Java JUC中操作List安全類的集合案例

    這篇文章主要介紹了JUC中操作List安全類的集合案例,本文羅列了不安全的集合和安全的集合進(jìn)行對比,以及Java中提供的安全措施,需要的朋友可以參考下
    2021-07-07
  • dubbo入門指南及demo實(shí)例詳解

    dubbo入門指南及demo實(shí)例詳解

    Dubbo是一個(gè)分布式服務(wù)框架,致力于提供高性能和透明化的RPC遠(yuǎn)程服務(wù)調(diào)用方案,以及SOA服務(wù)治理方案,這篇文章主要介紹了dubbo詳解及demo實(shí)例,需要的朋友可以參考下
    2022-04-04
  • 基于@ComponentScan注解的使用詳解

    基于@ComponentScan注解的使用詳解

    這篇文章主要介紹了@ComponentScan注解的使用詳解,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • 解決springboot URL帶有斜杠的轉(zhuǎn)義字符百分之2F導(dǎo)致的400錯(cuò)誤

    解決springboot URL帶有斜杠的轉(zhuǎn)義字符百分之2F導(dǎo)致的400錯(cuò)誤

    這篇文章主要介紹了解決springboot URL帶有斜杠的轉(zhuǎn)義字符百分之2F導(dǎo)致的400錯(cuò)誤問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08

最新評論