精华内容
下载资源
问答
  • 异步请求与异步调用的区别 两者的使用场景不同,异步请求用来解决并发请求对服务器造成的压力,从而提高对请求的吞吐量;而异步调用是用来做一些非主线流程且不需要实时计算和响应的任务,比如同步日志到kafka中做...

    异步请求与异步调用的区别

    两者的使用场景不同,异步请求用来解决并发请求对服务器造成的压力,从而提高对请求的吞吐量;而异步调用是用来做一些非主线流程且不需要实时计算和响应的任务,比如同步日志到kafka中做日志分析等。

    异步请求是会一直等待response相应的,需要返回结果给客户端的;而异步调用我们往往会马上返回给客户端响应,完成这次整个的请求,至于异步调用的任务后台自己慢慢跑就行,客户端不会关心。

     

     

    1、异步请求的实现

    方式一:Servlet方式实现异步请求

      @RequestMapping(value = "/email/servletReq", method = GET)
      public void servletReq (HttpServletRequest request, HttpServletResponse response) {
          AsyncContext asyncContext = request.startAsync();
          //设置监听器:可设置其开始、完成、异常、超时等事件的回调处理
          asyncContext.addListener(new AsyncListener() {
              @Override
              public void onTimeout(AsyncEvent event) throws IOException {
                  System.out.println("超时了...");
                  //做一些超时后的相关操作...
              }
              @Override
              public void onStartAsync(AsyncEvent event) throws IOException {
                  System.out.println("线程开始");
              }
              @Override
              public void onError(AsyncEvent event) throws IOException {
                  System.out.println("发生错误:"+event.getThrowable());
              }
              @Override
              public void onComplete(AsyncEvent event) throws IOException {
                  System.out.println("执行完成");
                  //这里可以做一些清理资源的操作...
              }
          });
          //设置超时时间
          asyncContext.setTimeout(20000);
          asyncContext.start(new Runnable() {
              @Override
              public void run() {
                  try {
                      Thread.sleep(10000);
                      System.out.println("内部线程:" + Thread.currentThread().getName());
                      asyncContext.getResponse().setCharacterEncoding("utf-8");
                      asyncContext.getResponse().setContentType("text/html;charset=UTF-8");
                      asyncContext.getResponse().getWriter().println("这是异步的请求返回");
                  } catch (Exception e) {
                      System.out.println("异常:"+e);
                  }
                  //异步请求完成通知
                  //此时整个请求才完成
                  asyncContext.complete();
              }
          });
          //此时之类 request的线程连接已经释放了
          System.out.println("主线程:" + Thread.currentThread().getName());
      }
    

    方式二:使用很简单,直接返回的参数包裹一层callable即可,可以继承WebMvcConfigurerAdapter类来设置默认线程池和超时处理

      @RequestMapping(value = "/email/callableReq", method = GET)
      @ResponseBody
      public Callable<String> callableReq () {
          System.out.println("外部线程:" + Thread.currentThread().getName());
    
          return new Callable<String>() {
    
              @Override
              public String call() throws Exception {
                  Thread.sleep(10000);
                  System.out.println("内部线程:" + Thread.currentThread().getName());
                  return "callable!";
              }
          };
      }
    
      @Configuration
      public class RequestAsyncPoolConfig extends WebMvcConfigurerAdapter {
    
      @Resource
      private ThreadPoolTaskExecutor myThreadPoolTaskExecutor;
    
      @Override
      public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {
          //处理 callable超时
          configurer.setDefaultTimeout(60*1000);
          configurer.setTaskExecutor(myThreadPoolTaskExecutor);
          configurer.registerCallableInterceptors(timeoutCallableProcessingInterceptor());
      }
    
      @Bean
      public TimeoutCallableProcessingInterceptor timeoutCallableProcessingInterceptor() {
          return new TimeoutCallableProcessingInterceptor();
      }
    }
    

    方式三:和方式二差不多,在Callable外包一层,给WebAsyncTask设置一个超时回调,即可实现超时处理

        @RequestMapping(value = "/email/webAsyncReq", method = GET)
        @ResponseBody
        public WebAsyncTask<String> webAsyncReq () {
            System.out.println("外部线程:" + Thread.currentThread().getName());
            Callable<String> result = () -> {
                System.out.println("内部线程开始:" + Thread.currentThread().getName());
                try {
                    TimeUnit.SECONDS.sleep(4);
                } catch (Exception e) {
                    // TODO: handle exception
                }
                logger.info("副线程返回");
                System.out.println("内部线程返回:" + Thread.currentThread().getName());
                return "success";
            };
            WebAsyncTask<String> wat = new WebAsyncTask<String>(3000L, result);
            wat.onTimeout(new Callable<String>() {
    
                @Override
                public String call() throws Exception {
                    // TODO Auto-generated method stub
                    return "超时";
                }
            });
            return wat;
        }
    

    方式四:DeferredResult可以处理一些相对复杂一些的业务逻辑,最主要还是可以在另一个线程里面进行业务处理及返回,即可在两个完全不相干的线程间的通信。

    @RequestMapping(value = "/email/deferredResultReq", method = GET)
        @ResponseBody
        public DeferredResult<String> deferredResultReq () {
            System.out.println("外部线程:" + Thread.currentThread().getName());
            //设置超时时间
            DeferredResult<String> result = new DeferredResult<String>(60*1000L);
            //处理超时事件 采用委托机制
            result.onTimeout(new Runnable() {
    
                @Override
                public void run() {
                    System.out.println("DeferredResult超时");
                    result.setResult("超时了!");
                }
            });
            result.onCompletion(new Runnable() {
    
                @Override
                public void run() {
                    //完成后
                    System.out.println("调用完成");
                }
            });
            myThreadPoolTaskExecutor.execute(new Runnable() {
    
                @Override
                public void run() {
                    //处理业务逻辑
                    System.out.println("内部线程:" + Thread.currentThread().getName());
                    //返回结果
                    result.setResult("DeferredResult!!");
                }
            });
           return result;
        }
    

    二、SpringBoot中异步调用的使用

    1、介绍

    异步请求的处理。除了异步请求,一般上我们用的比较多的应该是异步调用。通常在开发过程中,会遇到一个方法是和实际业务无关的,没有紧密性的。比如记录日志信息等业务。这个时候正常就是启一个新线程去做一些业务处理,让主线程异步的执行其他业务。

    2、使用方式(基于spring下)

    需要在启动类加入@EnableAsync使异步调用@Async注解生效

    在需要异步执行的方法上加入此注解即可@Async("threadPool"),threadPool为自定义线程池

    代码略。。。就俩标签,自己试一把就可以了

    3、注意事项

    在默认情况下,未设置TaskExecutor时,默认是使用SimpleAsyncTaskExecutor这个线程池,但此线程不是真正意义上的线程池,因为线程不重用,每次调用都会创建一个新的线程。可通过控制台日志输出可以看出,每次输出线程名都是递增的。所以最好我们来自定义一个线程池。

    调用的异步方法,不能为同一个类的方法(包括同一个类的内部类),简单来说,因为Spring在启动扫描时会为其创建一个代理类,而同类调用时,还是调用本身的代理类的,所以和平常调用是一样的。

    其他的注解如@Cache等也是一样的道理,说白了,就是Spring的代理机制造成的。所以在开发中,最好把异步服务单独抽出一个类来管理。下面会重点讲述。

    4、什么情况下会导致@Async异步方法会失效?

    a.调用同一个类下注有@Async异步方法:在spring中像@Async和@Transactional、cache等注解本质使用的是动态代理,其实Spring容器在初始化的时候Spring容器会将含有AOP注解的类对象“替换”为代理对象(简单这么理解),那么注解失效的原因就很明显了,就是因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器,那么解决方法也会沿着这个思路来解决。

    b.调用的是静态(static )方法

    c.调用(private)私有化方法

    5、解决4中问题1的方式(其它2,3两个问题自己注意下就可以了)

    将要异步执行的方法单独抽取成一个类,原理就是当你把执行异步的方法单独抽取成一个类的时候,这个类肯定是被Spring管理的,其他Spring组件需要调用的时候肯定会注入进去,这时候实际上注入进去的就是代理类了。

    其实我们的注入对象都是从Spring容器中给当前Spring组件进行成员变量的赋值,由于某些类使用了AOP注解,那么实际上在Spring容器中实际存在的是它的代理对象。那么我们就可以通过上下文获取自己的代理对象调用异步方法。

    @Controller
    @RequestMapping("/app")
    public class EmailController {
    
        //获取ApplicationContext对象方式有多种,这种最简单,其它的大家自行了解一下
        @Autowired
        private ApplicationContext applicationContext;
    
        @RequestMapping(value = "/email/asyncCall", method = GET)
        @ResponseBody
        public Map<String, Object> asyncCall () {
            Map<String, Object> resMap = new HashMap<String, Object>();
            try{
                //这样调用同类下的异步方法是不起作用的
                //this.testAsyncTask();
                //通过上下文获取自己的代理对象调用异步方法
                EmailController emailController = (EmailController)applicationContext.getBean(EmailController.class);
                emailController.testAsyncTask();
                resMap.put("code",200);
            }catch (Exception e) {
                resMap.put("code",400);
                logger.error("error!",e);
            }
            return resMap;
        }
    
        //注意一定是public,且是非static方法
        @Async
        public void testAsyncTask() throws InterruptedException {
            Thread.sleep(10000);
            System.out.println("异步任务执行完成!");
        }
    
    }
    

    6、开启cglib代理,手动获取Spring代理类,从而调用同类下的异步方法。

    首先,在启动类上加上@EnableAspectJAutoProxy(exposeProxy = true)注解。

    代码实现,如下:

    @Service
    @Transactional(value = "transactionManager", readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
    public class EmailService {
    
        @Autowired
        private ApplicationContext applicationContext;
    
        @Async
        public void testSyncTask() throws InterruptedException {
            Thread.sleep(10000);
            System.out.println("异步任务执行完成!");
        }
    
    
        public void asyncCallTwo() throws InterruptedException {
            //this.testSyncTask();
    //        EmailService emailService = (EmailService)applicationContext.getBean(EmailService.class);
    //        emailService.testSyncTask();
            boolean isAop = AopUtils.isAopProxy(EmailController.class);//是否是代理对象;
            boolean isCglib = AopUtils.isCglibProxy(EmailController.class);  //是否是CGLIB方式的代理对象;
            boolean isJdk = AopUtils.isJdkDynamicProxy(EmailController.class);  //是否是JDK动态代理方式的代理对象;
            //以下才是重点!!!
            EmailService emailService = (EmailService)applicationContext.getBean(EmailService.class);
            EmailService proxy = (EmailService) AopContext.currentProxy();
            System.out.println(emailService == proxy ? true : false);
            proxy.testSyncTask();
            System.out.println("end!!!");
        }
    }
    展开全文
  • muduo异步日志总结

    2020-12-20 23:05:04
    同步日志与异步日志 同步日志:网络IO线程或业务线程直接向磁盘文件中写日志信息,只有等一条日志消息写完之后才能执行后续的程序。同步日志容易阻塞在磁盘IO上,效率较低且影响服务器性能,应尽量避免在服务器中多...

    muduo中的日志是指诊断日志,即通常用于故障诊断和追踪的日志,便于服务器发生故障时的线索追踪,是网络库中很重要的一个部分。
    在总结异步日志之前,首先应该清楚什么是异步日志?与同步日志又有什么区别?

    同步日志与异步日志

    同步日志:网络IO线程或业务线程直接向磁盘文件中写日志信息,只有等一条日志消息写完之后才能执行后续的程序。同步日志容易阻塞在磁盘IO上,效率较低且影响服务器性能,应尽量避免在服务器中多次使用磁盘IO。

    异步日志:网络IO线程或业务线程产生日志消息时,用一个缓冲区储存起来,等到合适的时机,用一个后台线程统一将日志消息写入磁盘文件中。异步日志避免了在网络IO线程或业务线程中阻塞在磁盘IO中,因此也称为非阻塞日志

    看了同步日志和异步日志的概念之后,使用异步日志的原因也很清晰了。异步日志避免了网络IO线程和业务线程中的磁盘IO,只在后台线程中使用了一次磁盘IO,大大提升了服务器的响应性能和日志信息的处理效率。

    muduo日志库的实现思想

    muduo日志库分为前端和后端两个部分,前端负责将生成的日志消息储存到缓冲区中,后端负责将缓冲区的日志消息写入到磁盘文件中。
    为了实现前端、后端的异步操作,同时避免前端每次生成日志消息都唤醒后端线程,从而提高日志处理效率,muduo日志库采用的是双缓冲技术,其实现思想为:准备两块缓冲区(记为buffer A、buffer B),前端负责往buffer A中写日志消息,后端负责将buffer B中的日志消息写入磁盘文件。当buffer A写满之后,后端线程中会交换buffer A和buffer B,让前端往buffer B中写入日志消息,后端将buffer A中的日志消息写到磁盘文件中,如此往复。同时,为了及时将生成的日志消息写入文件,便于管理者分析日志消息,即使buffer A未满,日志库也会每3秒执行交换写入操作。

    总结,muduo日志库的优点为:避免了前端每生成一条日志消息就传送给后端,而是将多条日志消息拼成一个大buffer传送给后端线程,相当于批量处理,减少了后端线程的唤醒频率,降低了服务器开销。

    muduo日志库关键代码分析

    muduo实现异步日志的类是AsyncLogging.h,声明的相关变量如下:

    typedef muduo::FixedBuffer<LargeBuffer> Buffer; //大小为4MB的缓冲区
    typedef std::unique_ptr<Buffer> BufferPtr;  //缓冲区指针
    typedef std::vector<BufferPtr> BufferVector;
    
    muduo::MutexLock mutex_;  //互斥锁,用于保证前端、后端的线程安全
    muduo::Condition cond_;  //条件变量
    BufferPtr currentBuffer_; //当前缓冲指针
    BufferPtr nextBuffer_;  //预备缓冲指针
    BufferVector buffers_;  //储存已填满的缓冲,并移交给后端
    

    前端的关键实现代码如下:

    void AsyncLogging::append(const char* logline, int len)//向当前缓冲区中添加日志消息,如果当前缓冲区放不下了,那么就把当前缓冲区放到前端缓冲区队列中
    {
        muduo::MutexLockGuard lock(mutex_);//用锁来保持同步
        if (currentBuffer_->avail() > len)//如果当前缓冲区还能放下当前日志消息
        {
            currentBuffer_->append(logline, len);//把日志消息添加到当前缓冲区中
        } 
        else//如果放不下,就把当前缓冲区移动到前端缓冲区队列中,然后用预备缓冲区来填充当前缓冲区
        { //将当前缓冲区放到前端缓冲区队列中后就要唤醒后端处理线程
            buffers_.push_back(std::move(currentBuffer_));
            if (nextBuffer_)//如果预备缓冲区还未使用,就用来填充当前缓冲区
            {
                currentBuffer_ = std::move(nextBuffer_);
            } 
            else//如果预备缓冲区无法使用,就重新分配一个新的缓冲区(如果日志写的速度很快,但是IO速度很慢,那么前端日志缓冲区就会积       //累,但是后端还没有来得及处理,此时预备缓冲区也还没有归还,就会产生这种情况
            {
                currentBuffer_.reset(new Buffer); // 很少发生
            }
            currentBuffer_->append(logline, len);//向新的当前缓冲区中写入日志消息
            cond_.notify(); //唤醒后台线程
        }
    

    前端实现的具体过程可描述为:网络IO线程或业务线程生成一条日志消息时,若当前缓冲currentBuffer_中剩余的空间大于日志消息的字节长度,就会把日志消息拷贝到当前缓冲中;否则,说明当前缓冲区已经写满,就把currentBuffer_移动到buffers_中,此时currentBuffer_=NULL,此后判断预备缓冲nextBuffer_是否为NULL。若currentBuffer_尚未使用,就把预备缓冲nextBuffer_移用为当前缓冲,然后继续向当前缓冲中追加日志消息并唤醒后端线程将缓冲中的日志消息写到磁盘文件中;若currentBuffer_=NULL,说明nextBuffer_已经被使用了,这时就需要重新分配一块缓冲区作为当前缓冲currentBuffer_,然后继续向当前缓冲中追加日志消息并唤醒后端线程将缓冲中的日志消息写到磁盘文件中(这种情况较少,只有在日志消息生成速度太快后端来不及写入日志消息时发生)。

    其中,buffers_为双缓冲技术中的buffer A,具体实现时buffers_根据日志消息的生成速度可调节大小,实现比较灵活。

    后端的关键实现代码为:

    void AsyncLogging::threadFunc() 
    {
      LogFile output(basename_, rollSize_, false);   //指定输出的日志文件
      BufferPtr newBuffer1(new Buffer);//用来填充移动后的currentBuffer_
      BufferPtr newBuffer2(new Buffer);//用来填充使用后的nextBuffer_
      
      newBuffer1->bzero(); //缓冲区清零
      newBuffer2->bzero(); //缓冲区清零
      BufferVector buffersToWrite;//后端缓冲区队列,初始大小为16
      buffersToWrite.reserve(16);
      while (running_)
      {
        {
          muduo::MutexLockGuard lock(mutex_);
          if (buffers_.empty())  //  如果前端缓冲区队列为空,就休眠flushInterval_的时间
          {
            cond_.waitForSeconds(flushInterval_);
          }
          buffers_.push_back(std::move(currentBuffer_));
    	  currentBuffer_ = std::move(newBuffer1); //当前缓冲区获取新的内存
          buffersToWrite.swap(buffers_); //前端缓冲区队列与后端缓冲区队列交换
          
          if (!nextBuffer_) //如果预备缓冲区为空,那么就使用newBuffer2作为预备缓冲区,保证始终有一个空闲的缓冲区用于预备
          {
            nextBuffer_ = std::move(newBuffer2);
          }
        }
     
        //如果最终后端缓冲区的缓冲区太多就只保留前三个
        if (buffersToWrite.size() > 25) 
        {
          char buf[256];//buf作为缓冲区太多时的错误提示字符串
          snprintf(buf, sizeof buf, "Dropped log messages at %s, %zd larger buffers\n",
                   Timestamp::now().toFormattedString().c_str(),
                   buffersToWrite.size()-2);
          fputs(buf, stderr);
          output.append(buf, static_cast<int>(strlen(buf)));//将buf写出到日志文件中
          buffersToWrite.erase(buffersToWrite.begin()+2, buffersToWrite.end());//只保留后端缓冲区队列中的前三个缓冲区
        }
     
        //遍历当前后端缓冲区队列中的所有缓冲区
        for (const auto& buffer : buffersToWrite)
        {
          // FIXME: use unbuffered stdio FILE ? or use ::writev ?
          output.append(buffer->data(), buffer->length());//依次写入日志文件
        }
    	//此时后端缓冲区中的日志消息已经全部写出,就可以重置缓冲区队列了
        if (buffersToWrite.size() > 2)
        {
          // drop non-bzero-ed buffers, avoid trashing
          buffersToWrite.resize(2);
        }
     
        if (!newBuffer1)//如果newBuffer1为空 (刚才用来替代当前缓冲了)
        {
          newBuffer1 = std::move(buffersToWrite.back()); //把后端缓冲区的最后一个作为newBuffer1
          buffersToWrite.pop_back(); //最后一个元素的拥有权已经转移到了newBuffer1中,因此删除最后一个元素
          newBuffer1->reset(); //重置newBuffer1为空闲状态
        }
     
        if (!newBuffer2)//如果newBuffer2为空
        {
          newBuffer2 = std::move(buffersToWrite.back());
          buffersToWrite.pop_back();
          newBuffer2->reset();
        }
     
        buffersToWrite.clear();//清空后端缓冲区队列
        output.flush();//清空文件缓冲区
      }
      output.flush();
    }
    

    后端实现的具体过程为:后端线程会准备两块临时缓冲buffer,然后执行一个循环。循环中首先判断buffers_是否为空,若为空就休眠flushInterval_秒再唤醒后端线程,休眠过程中,若前端中buffers_不为空,后端线程也会被唤醒。后端线程唤醒后,将前端的当前缓冲currentBuffer_中的数据追加到buffers_,并立即将空闲的newBuffer1移为当前缓冲。紧接着,交换buffers_与buffersToWrite的资源,并用newBuffer2替换nextBuffer_(保证前端始终有一个可调配的预备buffer)。以上步骤涉及到前端与后端的数据交互,为保证数据同步,需要加锁,使其处于临界区。

    临界区的数据交互完成之后,就可以将buffersToWrite中的日志数据写入磁盘文件。写日志消息时,若buffersToWrite过大,只保留前三个缓冲区的日志数据,从而避免日志堆积。

    日志数据写入完成以后,重置buffersToWrite的大小为2,并重新用于填充newBuffer1和newBuffer2,从而实现了现有资源的有效利用,避免了重新开辟内存资源,有利于提高服务器性能。

    最后,清空后端缓冲队列buffersToWrite,并flush缓冲区,从而促使缓冲区的日志数据全部写入到磁盘文件中。

    其中,buffersToWrite为双缓冲技术中的buffer B。

    总结

    muduo异步日志库的实现,有很多细节都体现了高性能服务器开发过程中如何提高服务器性能,读者可以从中多多学习。比如:
    一、互斥锁所持有临界区很小,临界区内只完成前端、后端日志数据的交互,然后前端继续向前端缓冲区写日志消息,后端也立即向文件中写数据。
    二、后端日志数据写完之后,会利用buffersToWrite中现有的内存空间来重新填充newBuffer1和newBuffer2,而不是重新开辟一块内存,这实际上也有利于节省内存开销,尽量减小日志消息给服务器性能造成的影响。

    展开全文
  • 首先 声明一点 也是初学者 多发生的错误问题 同步异步的回调地址是必须公网访问的地址 不是局域网的地址 如果本机测试 公司允许的情况下 可以使用 ngrock 或者花生壳等软件 进行公网映射。如果不可以那么只能 在...

    首先 声明一点 也是初学者 多发生的错误问题 同步和异步的回调地址是必须公网访问的地址 不是局域网的地址 如果本机测试 公司允许的情况下 可以使用 ngrock 或者花生壳等软件 进行公网映射。如果不可以那么只能 在公网服务器测接口 多写日志 看日志打印了

    //同步通知
    return_url (跳转页面,买家支付成功后跳转的页面,仅当买家支付成功后跳转一次。)
    //异步通知
    notify_url (异步通知,下单成功后,支付宝服务器通知商户服务,并把这笔订单的状态通知给商户,商户根据返回的这笔订单的状态,修改网站订单的状态,比如等待买家付款状态,买家已经付款等待卖家发货…)

    return_url 与 notify_url 的区别

    买家付款成功后,如果接口中指定有return_url ,买家付完款后会跳到 return_url所在的页面,这个页面可以展示给客户看,这个页面只有付款成功才会跳转.

    notify_url:服务器后台通知,这个页面是程序后台运行的(买家和卖家都看不到),买家付完款后,支付宝会调用notify_url这个页面所在的页面并把相应的参数传递到这个页面,这个页面根据支付宝传递过来的参数修改网站订单的状态,更新完订单后需要在页面上打印出一个success给支付宝,如果反馈给支付宝的不是success,支付宝会继续调用这个页面.
    流程:买家付完款(trade_status=WAIT_SELLER_SEND_GOODS)—>支付宝通知notify_url—& gt;如果反馈给支付宝的是success(表示成功,这个状态下不再反馈,如果不是继续通知,一般第一次发送和第二次发送的时间间隔是3分钟)
    剩下的过程,卖家发货,买家确认收货,交易成功都是这个流程

    展开全文
  • 异步复制:主把事务写到binlog日志并不管从是否接收或者什么时候接收,commit之后,不会从发生ack之类的交互。 同步复制:当主提交一个事务,在主向前端返回一个commit成功的指令前,必须保证所有的从已经提交了...

    异步复制:主把事务写到binlog日志并不管从是否接收或者什么时候接收,commit之后,不会与从发生ack之类的交互。

    同步复制:当主提交一个事务,在主向前端返回一个commit成功的指令前,必须保证所有的从已经提交了这个事务(所有从不但接收了,还必须apply了该事务日志)

    半同步复制:(5.7.2版本之前默认且唯一的一个参数值after_commit):主上客户端发出提交指令,事务提交到了存储引擎后,等待从传递过来ack,再向前端返回成功的状态。
    与无损复制的区别就是:如果在主上这个事务已经提交到了存储引擎,而正在等待从的ack过程中---这个时候发生creash,则主上这个事务其实已经认为commit了,而从还没commit,
    在切换到从后,就会回滚最后的这个事务,这个时候主从的时候其实就不一致了)

    无损复制:(5.7.2版本之后一个参数值after_sync):主上客户端发出提交指令,事务写入到了binlog,传递到了从(事务写入到了relay.log且flush to disk中持久化到了磁盘的relay.log中)
    然后从给主反馈一个ack,master才会把事务提交到存储引擎且返回到client一个commit成功的指令

    展开全文
  • 一、异步请求与异步调用的区别 两者的使用场景不同: 异步调用是用来做一些非主线流程且不需要实时计算和响应的任务,比如同步日志到kafka中做日志分析,或保存历史数据等。 异步请求用来解决并发请求对服务器造成的...
  • 文章目录前言一、认识Log4j21.1、介绍Log4j21.2、日志等级(6个)二、配置文件三、实际应用3.1、使用log4j2的日志门面3.2、使用slf4j+log4j2(推荐使用)四、异步日志4.1、介绍异步日志简单介绍同步与异步区别性能比对...
  • 技术日志——第三篇

    2021-03-05 22:45:40
    工训竞赛物流车技术日志2021.3.5uart和usart的区别激光测距 uart和usart的区别 USART(universal synchronous asynchronous receiver and transmitte): 通用同步异步收发器 USART是一个串行通信设备,可以灵活地...
  • **异步请求与异步调用的区别** - 两者的使用场景不同,**异步请求**用来解决`并发请求对服务器造成的压力`,从而`提高对请求的吞吐量`;而**异步调用**是用来`做一些非主线流程且不需要实时计算和响应的任务`,比如...
  • stream,是oracle自开发的数据同步工具,免费,功能也强大,异步,通过捕获归档日志,转化为易于传播的LCR, LCR组合为queue, 由传播线程传播到目的数据库。 可以在捕获和传播之前应用rule,来指定哪些集合的数据
  • MQ和feign的区别

    2021-03-30 08:52:22
    特点:面向数据,生产者消费者,有缓冲节点,异步,系统级、模块级通信 具体: 消息的发送者和消费者需要解耦 发送者不明确也不关心谁是消费者 消费者不止一个,各种消费者可以从不同角度处理消息,消息的处理...
  • 2.6 公共变量属性的区别 93 2.7 参数修饰符params、out和ref的区别 96 2.8 值类型和引用类型的区别 100 2.9 结构类的区别 103 2.10 Dispose()和Close()、Finalize()的 区别 106 2.11 string和StringBuilder有...
  • 2.6 公共变量属性的区别 93 2.7 参数修饰符params、out和ref的区别 96 2.8 值类型和引用类型的区别 100 2.9 结构类的区别 103 2.10 Dispose()和Close()、Finalize()的 区别 106 2.11 string和StringBuilder有...
  • 2.6 公共变量属性的区别 93 2.7 参数修饰符params、out和ref的区别 96 2.8 值类型和引用类型的区别 100 2.9 结构类的区别 103 2.10 Dispose()和Close()、Finalize()的 区别 106 2.11 string和StringBuilder有...
  • 2.6 公共变量属性的区别 93 2.7 参数修饰符params、out和ref的区别 96 2.8 值类型和引用类型的区别 100 2.9 结构类的区别 103 2.10 Dispose()和Close()、Finalize()的 区别 106 2.11 string和StringBuilder有...
  • 1、flumekafka的区别 flume适合做日志采集,可以定制多种数据源,减少开发量;而kafka是分布式消息处理的中间件,自带存储功能,适合做日志缓存;flume主要用于将数据往HDFS、HBASE发送;如果涉及多个系统的使用,...
  • 消息中间键

    2018-02-28 16:00:21
    中间键传统传输方式区别异步,无需等待同步概念:问题1,当a项目调用b项目时,b项目有延时会产生什么场景 a会一直等待b项目响应,阻塞,费时,还可能在等待响应时间产生重复提交 小知识:防止接口重复提交 可以...
  • SqlServer 更改跟踪(Chang Tracking)

    千次阅读 2015-05-19 01:22:46
    对于跟踪数据库表的 DML 操作,SQLserver 2008 及以上版本提供了 变更数据捕获和更改跟踪。 变更数据库捕获 跟踪更改 的...变更数据捕获使用异步进程捕获,该进程扫描事务日志;更改跟踪同步跟踪DML操作 更多参考
  • 概念:两台以上的数据库示例,通过 binlog 二进制日志实现数据“同步”关系(属于异步工作模式) 条件:需要两台以上的数据库实例(时间同步 网络通畅),server_id 不同,区别不同的角色;主库开启binlog,建立专用...
  • 一、 基础概念1. 什么是定时任务定时任务形式:每隔一定时间/特定某一时刻执行场景:订单审核、出库订单超时自动... 定时任务消息队列的区别共同点异步处理:比如注册、下单事件应用解耦: 不管定时任务作业还是M...
  • 第01节、Http协议概述&同步与异步区别 第02节、Http请求头分析 第03节、使用时间戳解决浏览器缓存问题 第04节、Http反盗链技术概念 第05节、Http防盗链技术代码实现 第06节、重定向实现原理 资料+源码.rar 0017-...
  • 8、同步异步怎么理解?分别在什么情况下使用。 26 9、java后台如何接收ajax数据? 26 10、为什么要有jquery? 26 11、jQuery选择器有多少种? 27 13、你是如何使用jquery中的ajax的? 27 14、jquery中的$.get和$....
  • 《淘东电商项目(54) -银联支付案例(同步与异步)》 《淘东电商项目(55) -支付系统核心表设计》 《淘东电商项目(56) -支付系统分布式事务的解决方案》 《淘东电商项目(57) -聚合支付(支付令牌接口)》 《淘...
  • logback日志集成配置 003-log4j2 【日志】集成log4j2 log4j2日志集成配置 004-bean 【bean】使用姿势 bean三种定义姿势 bean三种注入方式 005-autoconfig 【bean】自动加载 @Configuration自动加载配置类...
  • 02 并发并行与同步异步的概念 03 GIL的概念 04 同步锁 05 递归锁 06 同步对象event 07 信号量 08 线程队列 09 生产者消费者模型 10 多进程的调用 第35章 01 进程通信 02 进程池 03 协程 04 事件驱动模型 05 IO模型...
  • github上的博客csdn的同步更新 image 技术相关的图片 javadoc 博客 sso-cookie 跨域读写cookie的例子 思维导图 和java相关的 更多干货 分布式实战(干货) spring cloud 实战(干货) mybatis 实战...
  • redis-3.2.0-win64

    2016-06-16 21:15:02
    SETNX key value SETNXSET的区别是SET可以创建更新key的value,而SETNX是如果key不存在,则创建keyvalue数据 MSET key1 value1 key2 value2 … keyN valueN 在一次原子操作下一次性设置多个键和值 MSETNX ...
  • 而且fs模块提供了同步(阻塞)和异步(非阻塞)两套处理方法(区别于方法名后是否有Sync) 如下阻塞方式的代码: <pre><code> js var fs = require('fs'); var con1 = fs.readFileSync('1.txt',&#...
  • NIO:同时支持阻塞非阻塞模式,但这里我们以其同步非阻塞I/O模式来说明,并且是面向缓冲区的 AIO:异步非阻塞I/O模型 详情移步:https://github.com/higminteam/practice/blob/master/src/main/java/com/practice/io...
  • synchronized (关键字) 同步(锁) ['siŋkrәnaiz] Thread [java] 线程 [θred] throw (关键字) throws (关键字) [θrәu] 抛出(异常) transient (关键字) 瞬变;临时的['trænziәnt]'(可序列化) valid 正确的,...

空空如也

空空如也

1 2 3
收藏数 54
精华内容 21
关键字:

同步日志与异步日志区别