SpringBoot工程中异步(Async)操作实践

文章目录

    • 导入
    • SpringBoot工程异步实践
      • 概述
      • 启动异步
      • 定义异步切入点方法
      • 自定义线程池的配置
    • SpringBoot工程中事务控制
      • 事务控制
      • 事务控制原理
    • SpringBoot 工程中三大JAVAEE组件注册及应用
      • 监听器(Listener)
        • 定义监听器
        • 注册监听器
      • 过滤器(Filter)
        • 定义过滤器
        • 注册过滤器
      • 处理器(Servlet)
        • 定义处理器
        • 注册处理器


导入

当项目中的一些非核心业务运行时,影响到用户核心业务的响应时间,导致用户体验下降,这个时候异步操作方案就因由而生了。异步操作方案就是指将非核心业务的耗时操作放到新的线程中异步执行。例如:

new Thread(new Runnable() {
     @Override
     public void run() {
        //耗时操作
     }
 }).start();

在并发比较小的时候是可以的,但是一旦并发量比较大时,反复创建线程和销毁线程会带来很大系统开销,进而影响整体性能。

SpringBoot工程异步实践

概述

SpringBoot 工程中默认支持异步操作,通过异步操作提高核心业务的响应速度。

启动异步

我们可以在SpringBoot工程启动类的上面,添加启动异步操作的注解(@EnableAsync)描述,代码如下:

@EnableAsync
@SpringBootApplication
public class AsyncApplication {
    public static void main(String[] args) {
        SpringApplication.run(AsyncApplication.class, args);
    }
}

定义异步切入点方法

假如此时某个业务方法需要执行异步操作,可以使用@Async注解对方法进行描述,例如写日志的业务,关键代码如下:

@Async
public void saveLog(SysLog entity){
    sysLogDao.insertLog(entity);
}

其中,@Async注解描述的方法,在Spring中会认为这是一个异步切入点方法, 在这个切入点方法执行时,底层会通过通知方法获取线程池中的线程,通过池中的线程调用切入点方法(底层默认池类型为ThreadPoolExecutor类型)

假如异步方法有返回值,可以采用AsyncResult对返回值进行封装,例如:

@Async
public Future<Integer> saveLog(SysLog entity){
    int result=sysLogDao.insertLog(entity);
    return new AsyncResult<Integer>(result);
}

虽然Future可以用于描述一个异步计算的结果,但是这个结果值的获取还是需要阻塞获取。如果阻塞获取其实没意义了,本来想用异步就是为了不阻塞,所以一般建议阻塞方法不写具体返回值。

自定义线程池的配置

SpringBoot工程在启动时,会默认配置一个线程池,当默认的线程池配置,不满足我们实际项目需求时,我们可以对线程池进行自定义的配置,SpringBoot配置文件application.properties(或者application.yml)中的关键配置如下:

spring.task.execution.pool.core-size=8
spring.task.execution.pool.max-size=256
spring.task.execution.pool.keep-alive=60000
spring.task.execution.pool.queue-capacity=512
spring.task.execution.thread-name-prefix=async-service-task-
  • core-size :核心线程数,当池中线程数没达到core-size的值时,每接收一个新的任务都会创建一个新线程,然后存储到池。假如池中线程数已经达到core-size设置的值,再接收新的任务时,要检测是否有空闲的核心线程,假如有,则使用空闲的核心线程执行新的任务。
  • queue-capacity:队列容量,假如核心线程数已达到core-size设置的值,并且所有的核心线程都在忙,再来新的任务,会将任务存储到任务队列。
  • max-size: 最大线程数,当任务队列已满,核心线程也都在忙,再来新的任务则会创建新的线程,但所有线程数不能超过max-size设置的值,否则可能会出现异常(拒绝执行)
  • keep-alive:线程空闲时间,假如池中的线程数多余core-size设置的值,此时又没有新的任务,则一旦空闲线程空闲时间超过keep-alive设置的时间值,则会被释放。
  • thread-name-prefix:线程名的前缀,项目中设置线程名的目的主要是为了对线程进行识别,一旦出现线程问题,可以更好的定位问题。

SpringBoot工程中事务控制

Spring 框架中提供了一种声明式事务的处理方式,此方式基于AOP代理,可以将具体业务逻辑与事务处理进行解耦。也就是让我们的业务代码逻辑不受污染或少量污染,就可以实现事务控制。

事务控制

SpringBoot工程中,可以通过@Transactional注解描述业务类或方法,进而告诉Spring框架,类中的所有方法或某些方法为切入点方法。这些方法执行时会进行事务控制。例如:

@Transactional(readOnly = false,rollbackFor = Throwable.class)
public class XxxServiceImpl implements XxxService{
  @Transactional(readOnly=true) //方法上的事务控制策略优先级高
  int deleteById(Long... id)
}

其中,@Transactional常用属性有:

  • timeout:事务的超时时间,默认值为-1,表示没有超时显示。如果配置了具体时间,则 超过该时间限制但事务还没有完成,则自动回滚事务。这个时间的记录方式是在事务 开启以后到 sql 语句执行之前。
  • read-only:指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的 方法,比如读取数据,可以设置 read-only 为 true。对添加,修改,删除业务 readonly 的值应该为 false。
  • rollback-for:用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指 定,各类型之间可以通过逗号分隔。
  • no-rollback- for: 抛出 no-rollback-for 指定的异常类型,不回滚事务。
  • isolation 事务的隔离级别,默认值采用 DEFAULT。当多个事务并发执行时,可能会 出现脏读,不可重复读,幻读等现象时,但假如不希望出现这些现象可考虑修改事务 的隔离级别(但隔离级别越高并发就会越小,性能就会越差)

事务控制原理

SpringBoot工程中异步(Async)操作实践_第1张图片

SpringBoot 工程中三大JAVAEE组件注册及应用

三大组件指的是:监听器(Listener)、过滤器(Filter)与处理器(Servlet)。
使用的时候都是,先定义,然后在注册到spring中(使用@bean的方法注入)。

监听器(Listener)

定义监听器

import org.springframework.stereotype.Component;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
 * 这里的监听器用于监听服务的启动和销毁
 */

public class DemoListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("服务启动了");
    }
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("服务销毁了");
    }
}

注册监听器

/**
 * 在这里配置javaee规范中的三大组件
 */
@Configuration
public class ComponentConfig {
    ......
    //注册监听器
    @Bean
    public ServletListenerRegistrationBean servletListenerRegistrationBean(){
        ServletListenerRegistrationBean bean=
                new ServletListenerRegistrationBean(new DemoListener());
        return bean;
    }
}

过滤器(Filter)

定义过滤器

package com.jt.filter;
import javax.servlet.*;
import java.io.IOException;
/**
 * javaee规范中的过滤器,对请求和响应数据进行过滤
 * 1)统一数据的编码
 * 2)统一数据格式校验 (今日头条的灵犬系统)
 * 3)统一身份认证
 */
public class DemoFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest,
                         ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        System.out.println("==doFilter==");
        servletRequest.setCharacterEncoding("UTF-8");
        String id= servletRequest.getParameter("id");
        System.out.println("id="+id);
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

注册过滤器

package com.jt.config;

/**
 * 在这里配置javaee规范中的三大组件
 */
@Configuration
public class ComponentConfig {
    ....
    //注册过滤器
    @Bean
    public FilterRegistrationBean filterRegistrationBean(){
        FilterRegistrationBean bean=
                new FilterRegistrationBean(new DemoFilter());
        bean.addUrlPatterns("/hello");//对哪个请求进行处理
        return bean;
    }
}

处理器(Servlet)

定义处理器

package com.jt.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * JavaEE规范中的Servlet
 */
public class DemoServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("==doGet==");
        resp.getWriter().println("hello client");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
    //...
}

注册处理器

package com.jt.config;

/**
 * 在这里配置javaee规范中的三大组件
 */
@Configuration
public class ComponentConfig {
    .....
    //注册servlet
    @Bean
    public ServletRegistrationBean servletRegistrationBean(){
        ServletRegistrationBean bean=new ServletRegistrationBean();
        bean.setServlet(new DemoServlet());
        bean.addUrlMappings("/hello");
        return bean;
    }
}

你可能感兴趣的:(spring,boot,spring,boot,java,spring)