范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

CompletableFuture实现异步编排全面分析和总结

  一、CompletableFuture简介CompletableFuture结合了Future的优点,提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合CompletableFuture的方法。
  CompletableFuture被设计在Java中进行异步编程。异步编程意味着在主线程之外创建一个独立的线程,与主线程分隔开,并在上面运行一个非阻塞的任务,然后通知主线程进展,成功或者失败。
  CompletableFuture是由Java8引入的,在Java8之前我们一般通过Future实现异步。
  Future用于表示异步计算的结果,只能通过阻塞或者轮询的方式获取结果,而且不支持设置回调方法,Java8之前若要设置回调一般会使用guava的ListenableFuture。 CompletableFuture对Future进行了扩展,可以通过设置回调的方式处理计算结果,同时也支持组合操作,支持进一步的编排,同时一定程度解决了回调地狱的问题。✔本文的名词缩写:CF:代表CompletableFutureCS:代表CompletionStage二、CompletableFuture 核心接口API介绍2.1 Future使用Future局限性
  从本质上说,Future表示一个异步计算的结果。它提供了isDone()来检测计算是否已经完成,并且在计算结束后,可以通过get()方法来获取计算结果。在异步计算中,Future确实是个非常优秀的接口。但是,它的本身也确实存在着许多限制:并发执行多任务:Future只提供了get()方法来获取结果,并且是阻塞的。所以,除了等待你别无他法;无法对多个任务进行链式调用:如果你希望在计算任务完成后执行特定动作,比如发邮件,但Future却没有提供这样的能力;无法组合多个任务:如果你运行了10个任务,并期望在它们全部执行结束后执行特定动作,那么在Future中这是无能为力的;没有异常处理:Future接口中没有关于异常处理的方法;
  方法
  说明
  描述
  boolean
  cancel (boolean mayInterruptIfRunning)
  尝试取消执行此任务。
  V
  get()
  如果需要等待计算完成,然后检索其结果。
  V
  get(long timeout, TimeUnit unit)
  如果需要,最多等待计算完成的给定时间,然后检索其结果(如果可用)。
  boolean
  isCancelled()
  如果此任务在正常完成之前取消,则返回 true 。
  boolean
  isDone()
  如果此任务完成,则返回 true 。2.2 CompletableFuturepublic class CompletableFuture implements Future, CompletionStage {  }
  JDK1.8 才新加入的一个实现类CompletableFuture,而CompletableFuture实现了两个接口(如上面代码所示):Future、CompletionStage,意味着可以像以前一样通过阻塞或者轮询的方式获得结果。
  Future表示异步计算的结果,CompletionStage用于表示异步执行过程中的一个步骤Stage,这个步骤可能是由另外一个CompletionStage触发的,随着当前步骤的完成,也可能会触发其他一系列CompletionStage的执行。从而我们可以根据实际业务对这些步骤进行多样化的编排组合,CompletionStage接口正是定义了这样的能力,我们可以通过其提供的thenAppy、thenCompose等函数式编程方法来组合编排这些步骤。CompletableFuture是Future接口的扩展和增强。CompletableFuture实现了Future接口,并在此基础上进行了丰富地扩展,完美地弥补了Future上述的种种问题。更为重要的是,CompletableFuture实现了对任务的编排能力。借助这项能力,我们可以轻松地组织不同任务的运行顺序、规则以及方式。从某种程度上说,这项能力是它的核心能力。而在以往,虽然通过CountDownLatch等工具类也可以实现任务的编排,但需要复杂的逻辑处理,不仅耗费精力且难以维护。2.3 CompletionStage
  CompletionStage接口提供了更多方法来更好的实现异步编排,并且大量的使用了JDK8引入的函数式编程概念。由stage执行的计算可以表示为Function,Consumer或Runnable(使用名称分别包括apply 、accept或run的方法 ),具体取决于它是否需要参数和/或产生结果。 例如:stage.thenApply(x -> square(x)).thenAccept(x -> System.out.print(x)).thenRun(() -> System.out.println());  三、使用CompletableFuture场景3.1 应用场景
  1️⃣ 执行比较耗时的操作时,尤其是那些依赖一个或多个远程服务的操作,使用异步任务可以改善程序的性能,加快程序的响应速度;
  2️⃣ 使用CompletableFuture类,它提供了异常管理的机制,让你有机会抛出、管理异步任务执行种发生的异常;
  3️⃣ 如果这些异步任务之间相互独立,或者他们之间的的某一些的结果是另一些的输入,你可以讲这些异步任务构造或合并成一个。
  举个常见的案例,在APP查询首页信息的时候,一般会涉及到不同的RPC远程调用来获取很多用户相关信息数据,比如:商品banner轮播图信息、用户message消息信息、用户权益信息、用户优惠券信息 等,假设每个rpc invoke()耗时是250ms,那么基于同步的方式获取到话,算下来接口的RT至少大于1s,这响应时长对于首页来说是万万不能接受的,因此,我们这种场景就可以通过多线程异步的方式去优化。
  3.2 CompletableFuture依赖链分析
  根据CompletableFuture依赖数量,可以分为以下几类:零依赖、单依赖、双重依赖和多重依赖 。
  零依赖
  下图Future1、Future2都是零依赖的体现:
  单依赖:仅依赖于一个CompletableFuture
  下图Future3、Future5都是单依赖的体现,分别依赖于Future1和Future2:
  双重依赖:同时依赖于两个CompletableFuture
  下图Future4即为双重依赖的体现,同时依赖于Future1和Future2:
  多重依赖:同时依赖于多个CompletableFuture
  下图Future6即为多重依赖的体现,同时依赖于Future3、Future4和Future5:
  类似这种多重依赖的流程来说,结果依赖于三个步骤:Future3、Future4、Future5,这种多元依赖可以通过allOf()或anyOf()方法来实现,区别是当需要多个依赖全部完成时使用allOf(),当多个依赖中的任意一个完成即可时使用anyOf(),如下代码所示:CompletableFuture Future6 = CompletableFuture.allOf(Future3, Future4, Future5); CompletableFuture result = Future6.thenApply(v -> {     //这里的join并不会阻塞,因为传给thenApply的函数是在Future3、Future4、Future5全部完成时,才会执行 。     result3 = Future3.join();     result4 = Future4.join();     result5 = Future5.join();          // 返回result3、result4、result5组装后结果     return assamble(result3, result4, result5); }); 四、CompletableFuture异步编排
  在分析CompletableFuture异步编排之前,我跟大家理清一下CompletionStage接口下 (thenRun、thenApply、thenAccept、thenCombine、thenCompose)、(handle、whenComplete、exceptionally) 相关方法的实际用法和它们之间的区别是什么? 带着你的想法往下看吧!!!4.1 《异步编排API》thenRun:【执行】直接开启一个异步线程执行任务,不接收任何参数,回调方法没有返回值;thenApply:【提供】可以提供返回值,接收上一个任务的执行结果,作为下一个Future的入参,回调方法是有返回值的;thenAccept:【接收】可以接收上一个任务的执行结果,作为下一个Future的入参,回调方法是没有返回值的;thenCombine:【结合】可以结合不同的Future的返回值,做为下一个Future的入参,回调方法是有返回值的;thenCompose:【组成】将上一个Future实例结果传递给下一个实例中。✔异步回调建议使用自定义线程池/**  * 线程池配置  *  * @author: austin  * @since: 2023/3/12 1:32  */ @Configuration public class ThreadPoolConfig {      /**      * @Bean中声明的value不能跟定义的实例同名      *      */     @Bean(value = "customAsyncTaskExecutor")     public ThreadPoolTaskExecutor asyncThreadPoolExecutor() {         ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();         threadPoolTaskExecutor.setCorePoolSize(5);         threadPoolTaskExecutor.setMaxPoolSize(10);         threadPoolTaskExecutor.setKeepAliveSeconds(60);         threadPoolTaskExecutor.setQueueCapacity(2048);         threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);         threadPoolTaskExecutor.setThreadNamePrefix("customAsyncTaskExecutor-");         threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());         return threadPoolTaskExecutor;     }      @Bean(value = "threadPoolExecutor")     public ThreadPoolExecutor threadPoolExecutor() {         ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10, 60L, TimeUnit.SECONDS,                 new ArrayBlockingQueue<>(10000), new ThreadPoolExecutor.CallerRunsPolicy());         return threadPoolExecutor;     } }
  如果所有异步回调都会共用该CommonPool,核心与非核心业务都竞争同一个池中的线程,很容易成为系统瓶颈。手动传递线程池参数可以更方便的调节参数,并且可以给不同的业务分配不同的线程池,以求资源隔离,减少不同业务之间的相互干扰。所以,强烈建议你要根据不同的业务类型创建不同的线程池,以避免互相干扰。通过自定义线程池customAsyncTaskExecutor,后面不同的异步编排方法,我们可以通过指定对应的线程池。1️⃣ runAsync()、thenRun()@RestController public class CompletableFutureCompose {      @Resource     private ThreadPoolTaskExecutor customAsyncTaskExecutor;      @RequestMapping(value = "/thenRun")     public void thenRun() {         CompletableFuture.runAsync(() -> {             System.out.println("thread name:" + Thread.currentThread().getName() + " first step...");         }, customAsyncTaskExecutor).thenRun(() -> {             System.out.println("thread name:" + Thread.currentThread().getName() + " second step...");         }).thenRunAsync(() -> {             System.out.println("thread name:" + Thread.currentThread().getName() + " third step...");         });     } }
  接口输出结果:thread name:customAsyncTaskExecutor-1 first step... thread name:customAsyncTaskExecutor-1 second step... thread name:ForkJoinPool.commonPool-worker-3 third step... 2️⃣ thenApply()@RequestMapping(value = "/thenApply") public void thenApply() {     CompletableFuture.supplyAsync(() -> {         System.out.println("thread name:" + Thread.currentThread().getName() + " first step...");         return "hello";     }, customAsyncTaskExecutor).thenApply((result1) -> {         String targetResult = result1 + " austin";         System.out.println("first step result: " + result1);         System.out.println("thread name:" + Thread.currentThread().getName() + " second step..., targetResult: " + targetResult);         return targetResult;     }); }
  接口输出结果:thread name:customAsyncTaskExecutor-2 first step... first step result: hello // thenApply虽然没有指定线程池,但是默认是复用它上一个任务的线程池的 thread name:customAsyncTaskExecutor-2 second step..., targetResult: hello austin 3️⃣ thenAccept()@RequestMapping(value = "/thenAccept") public void thenAccept() {     CompletableFuture.supplyAsync(() -> {         System.out.println("thread name:" + Thread.currentThread().getName() + " first step...");         return "hello";     }, customAsyncTaskExecutor).thenAccept((result1) -> {         String targetResult = result1 + " austin";         System.out.println("first step result: " + result1);         System.out.println("thread name:" + Thread.currentThread().getName() + " second step..., targetResult: " + targetResult);     }); }
  接口输出结果:thread name:customAsyncTaskExecutor-3 first step... first step result: hello // thenAccept在没有指定线程池的情况下,并未复用它上一个任务的线程池 thread name:http-nio-10032-exec-9 second step..., targetResult: hello austin
  thenAccept()和thenApply()的用法实际上基本上一致,区别在于thenAccept()回调方法是没有返回值的,而thenApply()回调的带返回值的。
  细心的朋友可能会发现,上面thenApply()和thenAccept()请求线程池在不指定的情况下,两者的不同表现,thenApply()在不指定线程池的情况下,会沿用上一个Future指定的线程池customAsyncTaskExecutor,而thenAccept()在不指定线程池的情况,并没有复用上一个Future设置的线程池,而是重新创建了新的线程来实现异步调用。4️⃣ thenCombine()@RequestMapping(value = "/thenCombine") public void thenCombine() {     CompletableFuture future1 = CompletableFuture.supplyAsync(() -> {         System.out.println("执行future1开始...");         return "Hello";     }, asyncThreadPoolExecutor);     CompletableFuture future2 = CompletableFuture.supplyAsync(() -> {         System.out.println("执行future2开始...");         return "World";     }, asyncThreadPoolExecutor);     future1.thenCombine(future2, (result1, result2) -> {         String result = result1 + " " + result2;         System.out.println("获取到future1、future2聚合结果:" + result);         return result;     }).thenAccept(result -> System.out.println(result)); }
  接口访问,打印结果:thread name:customAsyncTaskExecutor-4 执行future1开始... thread name:customAsyncTaskExecutor-5 执行future2开始... thread name:http-nio-10032-exec-8 获取到future1、future2聚合结果:Hello World Hello World 复制代码5️⃣ thenCompose()
  我们先有future1,然后和future2组成一个链:future1 -> future2,然后又组合了future3,形成链:future1 -> future2 -> future3。这里有个隐藏的点:future1、future2、future3它们完全没有数据依赖关系,我们只不过是聚合了它们的结果。@RequestMapping(value = "/thenCompose") public void thenCompose() {     CompletableFuture.supplyAsync(() -> {         // 第一个Future实例结果         System.out.println("thread name:" + Thread.currentThread().getName() + " 执行future1开始...");         return "Hello";     }, customAsyncTaskExecutor).thenCompose(result1 -> CompletableFuture.supplyAsync(() -> {         // 将上一个Future实例结果传到这里         System.out.println("thread name:" + Thread.currentThread().getName() + " 执行future2开始..., 第一个实例结果:" + result1);         return result1 + " World";     })).thenCompose(result12 -> CompletableFuture.supplyAsync(() -> {         // 将第一个和第二个实例结果传到这里         System.out.println("thread name:" + Thread.currentThread().getName() + " 执行future3开始..., 第一第二个实现聚合结果:" + result12);         String targetResult = result12 + ", I am austin!";         System.out.println("最终输出结果:" + targetResult);         return targetResult;     })); }
  接口访问,打印结果:thread name:customAsyncTaskExecutor-1 执行future1开始... thread name:ForkJoinPool.commonPool-worker-3 执行future2开始..., 第一个实例结果:Hello thread name:ForkJoinPool.commonPool-worker-3 执行future3开始..., 第一第二个实现聚合结果:Hello World 最终输出结果:Hello World, I am austin! Note:thenCombine() VS thenCompose(),两者之间的区别
  thenCombine结合的两个CompletableFuture没有依赖关系,且第二个CompletableFuture不需要等第一个CompletableFuture执行完成才开始。 thenCompose() 可以两个 CompletableFuture 对象,并将前一个任务的返回结果作为下一个任务的参数,它们之间存在着先后顺序。 thenCombine() 会在两个任务都执行完成后,把两个任务的结果合并。两个任务是并行执行的,它们之间并没有先后依赖顺序。4.2 《CompletableFuture实例化创建》// 返回一个新的CompletableFuture,由线程池ForkJoinPool.commonPool()中运行的任务异步完成,不会返回结果。 public static CompletableFuture runAsync(Runnable runnable); // 返回一个新的CompletableFuture,运行任务时可以指定自定义线程池来实现异步,不会返回结果。 public static CompletableFuture runAsync(Runnable runnable, Executor executor);  // 返回由线程池ForkJoinPool.commonPool()中运行的任务异步完成的新CompletableFuture,可以返回异步线程执行之后的结果。 public static  CompletableFuture supplyAsync(Supplier supplier); public static  CompletableFuture supplyAsync(Supplier supplier, Executor executor);
  CompletableFuture有两种方式实现异步,一种是supply开头的方法,一种是run开头的方法:supply 开头:该方法可以返回异步线程执行之后的结果;run 开头:该方法不会返回结果,就只是执行线程任务。4.3 《获取CompletableFuture结果》public  T   get() public  T   get(long timeout, TimeUnit unit) public  T   getNow(T valueIfAbsent) public  T   join() public CompletableFuture allOf() public CompletableFuture anyOf()
  使用方式,演示:CompletableFuture future = new CompletableFuture<>(); Integer integer = future.get(); get():阻塞式获取执行结果,如果任务还没有完成则会阻塞等待知直到任务执行完成get(long timeout, TimeUnit unit):带超时的阻塞式获取执行结果getNow():如果已完成,立刻返回执行结果,否则返回给定的valueIfAbsentjoin():该方法和get()方法作用一样, 不抛异常的阻塞式获取异步执行结果allOf():当给定的所有CompletableFuture都完成时,返回一个新的CompletableFutureanyOf():当给定的其中一个CompletableFuture完成时,返回一个新的CompletableFutureNote:
  join()和get()方法都是 阻塞式 调用它们的线程(通常为主线程)来获取CompletableFuture异步之后的返回值。 两者的区别在于join()返回计算的结果或者抛出一个unchecked异常CompletionException,而get()返回一个具体的异常。4.4 《结果处理》
  当使用CompletableFuture异步调用计算结果完成、或者是抛出异常的时候,我们可以执行特定的Action做进一步处理,比如:public CompletableFuture whenComplete(BiConsumer<? super T,? super Throwable> action)  public CompletableFuture whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)  public CompletableFuture whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor) 4.5 《异常处理》
  使用CompletableFuture编写代码时,异常处理很重要,CompletableFuture提供了三种方法来处理它们:handle()、whenComplete() 和 exceptionly()。handle:返回一个新的CompletionStage,当该阶段正常或异常完成时,将使用此阶段的结果和异常作为所提供函数的参数执行,不会将内部异常抛出。whenComplete:返回与此阶段具有相同结果或异常的新CompletionStage,该阶段在此阶段完成时执行给定操作。与方法handle不同,会将内部异常往外抛出。exceptionally:返回一个新的CompletableFuture,CompletableFuture提供了异常捕获回调exceptionally,相当于同步调用中的try/catch。@Autowired private RemoteDictService remoteDictService;  public CompletableFuture getDictDataAsync(long dictId) {     CompletableFuture resultFuture = remoteDictService.findDictById(dictId);     // 业务方法,内部会发起异步rpc调用     return resultFuture             .exceptionally(error -> {                 //通过exceptionally捕获异常,打印日志并返回默认值                 log.error("RemoteDictService.getDictDataAsync Exception dictId = {}", dictId, error);                 return null;             }); } handle() VS whenComplete(), 两者之间的区别核心区别在于whenComplete不消费异常,而handle消费异常Two method forms support processing whether the triggering stage completed normally or exceptionally:
  Method {whenComplete} allows injection of an action regardless of outcome, otherwise preserving the outcome in its completion.
  Method {handle} additionally allows the stage to compute a replacement result that may enable further processing by other dependent stages.
  翻译过来就是:
  两种方法形式支持处理触发阶段是否 正常完成 或 异常完成:whenComplete:可以访问当前CompletableFuture的 结果 和 异常 作为参数,使用它们并执行您想要的操作。此方法并不能转换完成的结果,会内部抛出异常。handle:当此阶段正常或异常完成时,将使用此阶段的结果和异常作为所提供函数的参数来执行。当此阶段完成时,以 该阶段的结果 和 该阶段的异常 作为参数调用给定函数,并且函数的结果用于完成返回的阶段,不会把异常外抛出来。
  这里我通过代码演示一下:public class CompletableFutureExceptionHandler {      public static CompletableFuture handle(int a, int b) {         return CompletableFuture.supplyAsync(() -> a / b)                 .handle((result, ex) -> {                     if (null != ex) {                         System.out.println("handle error: " + ex.getMessage());                         return 0;                     } else {                         return result;                     }                 });     }      public static CompletableFuture whenComplete(int a, int b) {         return CompletableFuture.supplyAsync(() -> a / b)                 .whenComplete((result, ex) -> {                     if (null != ex) {                         System.out.println("whenComplete error: " + ex.getMessage());                     }                 });     }       public static void main(String[] args) {         try {             System.out.println("success: " + handle(10, 5).get());             System.out.println("fail: " + handle(10, 0).get());         } catch (Exception e) {             System.out.println("catch exception= " + e.getMessage());         }          System.out.println("------------------------------------------------------------------");          try {             System.out.println("success: " + whenComplete(10, 5).get());             System.out.println("fail: " + whenComplete(10, 0).get());         } catch (Exception e) {             System.out.println("catch exception=" + e.getMessage());         }     } }
  运行结果如下显示:success: 2 handle error: java.lang.ArithmeticException: / by zero fail: 0 ------------------------------------------------------------------ success: 2 whenComplete error: java.lang.ArithmeticException: / by zero catch exception=java.lang.ArithmeticException: / by zero
  ✔可以看到,handle处理,当程序发生异常的时候,即便是catch获取异常期望输出,但是并未跟实际预想那样,原因是handle不会把内部异常外抛出来,而whenComplete会将内部异常抛出。五、CompletableFuture线程池须知Note:关于异步线程池(十分重要)
  异步回调方法可以选择是否传递线程池参数Executor,这里为了实现线程池隔离,当不传递线程池时,默认会使用ForkJoinPool中的公共线程池CommonPool,这个线程池默认创建的线程数是CPU的核数,如果所有的异步回调共享一个线程池,核心与非核心业务都竞争同一个池中的线程,那么一旦有任务执行一些很慢的I/O 操作,就会导致线程池中所有线程都阻塞在I/O操作上,很容易成为系统瓶颈,影响整个系统的性能。因此, 建议强制传线程池,且根据实际情况做线程池隔离,减少不同业务之间的相互干扰。六、基于CompletableFuture实现接口异步revoke案例实现Controller层@RestController @RequestMapping("/index") public class IndexWebController {     @Resource     private ThreadPoolExecutor asyncThreadPoolExecutor;      @RequestMapping(value = "/homeIndex", method = {RequestMethod.POST, RequestMethod.GET})     public String homeIndex(@RequestParam(required = false) String userId, @RequestParam(value = "lang") String lang) {         ResultData result = new ResultData<>();         // 获取Banner轮播图信息         CompletableFuture> future1 = CompletableFuture.supplyAsync(() -> this.buildBanners(userId, lang), asyncThreadPoolExecutor);         // 获取用户message通知信息         CompletableFuture future2 = CompletableFuture.supplyAsync(() -> this.buildNotifications(userId, lang), asyncThreadPoolExecutor);         // 获取用户权益信息         CompletableFuture> future3 = CompletableFuture.supplyAsync(() -> this.buildBenefits(userId, lang), asyncThreadPoolExecutor);          // 获取优惠券信息         CompletableFuture> future4 = CompletableFuture.supplyAsync(() -> this.buildCoupons(userId), asyncThreadPoolExecutor);                  CompletableFuture allOfFuture = CompletableFuture.allOf(futrue1, futrue2, futrue3, future4);         HomeVo finalHomeVO = homeVO;         CompletableFuture resultFuture = allOfFuture.thenApply(v -> {             try {                 finalHomeVo.setBanners(future1.get());                 finalHomeVo.setNotifications(future2.get());                 finalHomeVo.setBenefits(future3.get());                 finalHomeVo.setCoupons(future4.get());                 return finalHomeVO;             } catch (Exception e) {                 logger.error("[Error] assemble homeVO data error: {}", e);                 throw new RuntimeException(e);             }         });         homeVO = resultFuture.join();         result.setData(homeVO);         return writeJson(result);     } } Service层@SneakyThrows public List buildBanners(String userId, String lang) {     // 模拟请求耗时0.5秒     Thread.sleep(500);     return new List(); }  @SneakyThrows public List buildNotifications(String userId, String lang) {     // 模拟请求耗时0.5秒     Thread.sleep(500);     return new List(); }  @SneakyThrows public List buildBenefits(String userId, String lang) {     // 模拟请求耗时0.5秒     Thread.sleep(500);     return new List(); }  @SneakyThrows public List buildCoupons(String userId) {     // 模拟请求耗时0.5秒     Thread.sleep(500);     return new List(); } 六、异步化带来的性能提升通过异步化改造,原本同步获取数据的API性能得到明显提升,大大减少了接口的响应时长(RT)。接口的吞吐量大幅度提升。七、总结
  本篇文章主要是介绍了CompletableFuture使用原理和相关不同方法的场景化使用,以及通过不同的实例演示了异步回调的过程,好了,今天的分享就到此结束了,如果文章对你有所帮助,欢迎 点赞+评论+收藏❤,
冬天的第一次旅行,来大悟十八潭吧悄悄的,冬天来了但是湖北十八潭景区的秋天似乎还未远去立冬后的十八潭依然被阳光捧的炙热在阳光明媚的时节为什么不来一次说走就走的旅行呢天然氧吧大峡谷五彩斑斓的十八潭大峡谷是一个丰富的生节育环进入了女性身体,会发生什么变化呢?痛苦才刚刚开始女性,自古以来都有一种被弱化的视觉。女本弱,为母则刚。她们一直都是弱势群体,但作为母亲的时候,她们有着超人的毅力。这个世界对女性是不公平的,让她们承受着十月怀胎到生孩子的痛苦,这是毛利率堪比贵州茅台,小酒馆第一股,海伦司,未来空间有多大?点赞了的2022发财,关注的年年发大财!本文是价值事务所的原创文章第1120篇。文章仅记录价值事务所思想,不构成投资建议,作者没有群不收费荐股不代客理财。都知道医疗是极好的赛道,各地评线飞天网评目标空间站,太空快递小哥再出发北京时间2022年11月12日10时03分,搭载天舟五号货运飞船的长征七号遥六运载火箭,在我国文昌航天发射场点火发射,天舟五号货运飞船与火箭成功分离并进入预定轨道,飞船太阳能帆板顺FENG娱2022丰台金秋文化旅游季秋赏丰台之旅纵情自然山水间今天,小FENG为大家推荐秋季旅游线路秋赏丰台之旅带你纵情于丰台的缤纷秋色第一站北京园博园北京园博园的秋,将园林的美渲染到了极致。北京园博园秋色正浓时,各大园林景观已经开启秋色滤镜助力创建天府旅游名县古蔺县举办旅游马拉松中新网四川新闻11月14日电(唐倩)为助力古蔺县创建天府旅游名县和全域旅游示范区,推动古蔺县全民健身和体旅产业融合发展,宣传古蔺县文旅形象和观文镇千鸟湖旅游目的地新品牌,提升古蔺县奶茶别出去买了,在家做简单更健康,无任何添加剂,低糖更好喝奶冻和奶茶是一对黄金搭档,相比那些奶茶椰果或者珍珠奶茶,口感要好上很多。毕竟搅一搅戳一戳,口感会更加细腻,弹牙感十足哦!而且颜值也是非常高的。今天就给大家做一款红糖冻奶茶,喝起来清张俪的笨蛋有弹性,新综艺名场面含量好高浪姐3播出结束之后,紧接着推出了衍生版音综乐队的海边,官宣海报中张俪身着一袭黑色长裙,黑发红唇,高挑窈窕身材展露无遗,不愧是冲上过热搜第一的美貌,我反手就把张俪漂亮打在公屏上!请记现在的双减政策,到底减轻了谁的负担?我们上学那会,父母从来都是不管不问,只看学习成绩,从来不辅导作业。可是现在呢?美名其约为了孩子有个美好快乐的童年,减轻他们的学习压力。是,这我也承认,确实现在的作业少了,有些地方甚爱吃面的朋友不要错过,8种家常汤面的做法,简单美味暖心又暖胃秋日生活打卡季没了烟火气,人生就是一段孤独的旅程。冬天的幸福不过一碗热腾腾的汤面,暖心又暖胃,爱吃面的朋友千万不要错过,分享给大家8种家常汤面的做法,快快收藏吧!浇汤面温度骤降,特官渡粑粑老昆明人挥之不去的美味念想昆明官渡粑粑官渡粑粑的历史文化渊源官渡粑粑的前身是胡麻饼,早在清朝年间就已享有盛名。因发源地是昆明官渡,故有官渡粑粑之说,现如今也是一代老昆明人的记忆。胡麻饼历史悠久的官渡镇位于昆
香港富商刘銮雄连夜关停300家内地公司,套现500亿举家移民英国国家对于税务问题一直十分看重,这几年光是娱乐圈明星就因为逃税问题,地震过许多回。比如著名的一天赚208w郑爽范冰冰的8亿税款以及薇娅被罚的13亿。这些明星即使日赚百万,也仍旧妄想逃日本为什么无法征服中国日本为什么打不赢二战聊下日本对中国占领区的经济消化二战时期德国和日本是两个最主要的法西斯国家,他们共同的弱点就是都是小国,其一是资本不够,其二是自然资源不够,其三是人力资源不够。战在南京玄奘寺供奉战犯的吴啊萍,到底是何许人也?又曝出新线索南京玄奘寺供奉战犯的事,让全国震怒!这些战犯的供奉者是一个化名叫吴啊萍的人供奉的从2018年至到2022年2月份,已经供奉了3年左右的时间其实她一共供奉着6个牌位,分别是1侵华日军崔宥莉直言中国女足实力强遗憾未能取胜,水庆霞或留有一手战日本东亚杯女足第二轮,中国女足11平韩国女足,保留夺冠希望北京时间7月23日的晚上6点,东亚杯女足比赛继续进行第二轮的较量。由中国女足迎战韩国女足,最终在韩国女足先取得领先的情况下,中南京玄奘寺什么来头,胆敢伤害14亿中国人民的感情?7月21日,有网友爆料称,位于南京九华山公园里面的玄奘寺,供奉着四名日军战犯的长生牌位,供奉人署名为吴啊萍。这四名战犯分别是南京大屠杀主要负责人之一的甲级战犯松井石根,南京大屠杀首00后充当出头鸟,同事的反应叫人失望,别再指望我们整顿职场不同年代的学生,因为成长环境和培养模式的不同,思维上也会带来很大的差别。有人说八零后是有担当的一代,九零后是任性的一代,零零后则更是让人感到天马行空,与众不同。事实上越来越多人发现德国教育部长反对因天然气短缺限制课堂活动据德国世界报网站8日报道,德国教育部长贝蒂娜施塔克瓦青格警告称,不能因为天然气供应短缺而限制课堂活动。她接受莱茵邮报采访说我在疫情期间就强调,学校也是重要的基础设施。她说,现在也应安倍晋三身亡,日本第一美女保镖面临失业,保镖这份工作现状如何北京时间7月8日,日本前首相安倍晋三在遭到枪击后,因伤势过重不治身亡。这是一个大事件,拜登普京莫迪等各国政要都表示了哀悼。逝者已矣,作为一个人力资源从业者,我在考虑的是安倍的美女保她被称最美校花,用证件照参加选美,被拍戏耽误的航天工程师校花的存在为大学生活增添了许多色彩,进入大学之后,同学们的爱美意识逐渐提升,所以在大学校园里,能够呈现出各种形形色色的人物,而最受欢迎的就是校花。大家对于校花的评判标准,首先最重要老戏骨吕丽萍怎么了?庆美国独立日又哭丧安倍晋三,评论区已沦陷7月8日中午,67岁日本前首相安倍晋三在街头演讲时遭遇暴力枪击事件,因伤势过重抢救无效宣告身亡消息一出,震惊世界各地!第一时间,港星余文乐在外网(Ins)发文悼念,配了一张黑白图及安倍晋三之死,夸大日本极右翼势力危害言过其实安倍晋三死了,如果日本右翼分子还想搞二战军国主义,恢复日本独立的军事指挥权,修改宪法脱离美国控制,建设一支世界级强大的军队就是痴人说梦。第一,日本目前的国力就不支持,当前人口老龄化