个人项目复习-短链Day01

考点1:注册功能要求

功能需求:使用手机号注册,且已经注册的手机号不能重复注册,密码不能使用简单的md5加密;用户上传头像需要用到文件存储。

安全需求:高并发下账号的唯一性注册邮箱或手机号验证码不能被恶意调用。(验证码+唯一索引);头像文件存储访问方便扩容和管理。(阿里云OSS);高并发处理:异步+池化思想。

考点2:压测工具对比

LoadRunner 性能稳定,压测结果及细粒度大,可以自定义脚本进行压测,但是太过于重大,功能比较繁多 Apache AB (单接口压测最方便): 模拟多线程并发请求。ab 命令对发出负载的计算机要求很低,既不会占用很多 CPU,也不会占用太多的内存,但却会给目标服务器造成巨大的负载,简单 DDOS 攻击等 Webbench: webbench 首先 fork 出多个子进程,每个子进程都循环做 web 访问测试。子进程把访问的结果通过 pipe 告诉父进程,父进程做最终的统计结果。 Jmeter **:**开源免费,功能强大,在互联网公司普遍使用

Jmeter的优点:

  1. 功能测试

  2. 压力测试

  3. 分布式压力测试

  4. 纯 java 开发

  5. 上手容易,高性能

  6. 提供测试数据分析

  7. 各种报表数据图形展示

考点3:@Async注解

什么是异步任务:异步调用时相对于同步调用而言的,同步调用时指程序按预定顺序一步步执行,每一步必须等到上一步执行完成后才能执行,异步调用则无需等待上一步程序执行完成即可执行。

异步任务适用在什么场景上:处理log,发送邮件,短信等,以及各种各样处理网络IO调用的操作上面。

@Async失效情况:

  1. 方法不是public

  2. 放回值只能为void或者Future

  3. 注解方法使用static修饰

  4. 没加注解@Async或者@EnableAsync注解

  5. 调用方与被调用方法不能在同一个类

考点4:为什么调用方法和被调用方法不能在同一个类?

  1. Spring在扫描bean的时候会扫描方法上是否包含@Async注解,动态地生产一个子类(即proxy代理类),当这个有注解的方法被调用时,实际上是由代理类来调用的,代理类在调用时增加异步作用。

  2. 如果这个有注解的方法是被同一个类中的其他方法调用的,那么该方法的调用并没有通过代理类,而是直接通过原来的那个bean,使用就失败了。

  3. 所以调用方和被调用方不能在用一个类,主要是使用了动态代理,同一个类的时候直接调用,不是通过生成的动态代理类调用。

  4. 一般将要异步执行的方法单独抽取成一个类。

  5. 类中需要使用@Autowired和@Resource等注解自动注入,不能自己动手new对象。

  6. 在Async方法上标注@Transactional是没有用的,但在Async方法上调用的方法上标注@Transactional是有效的。

考点5:自定义线程池

为什么要自定义 线程池

当我们使用@Async注解没有指定线程池的话,即未设置TaskExecutor时,默认使用Spring创建ThreadPoolTaskExecutor,默认线程数为8,最大线程数为:Integer。MAX_VALUE(21亿多),队列使用LinkedBlockingQueue,容量是Inter.MAX_VALUE,空闲线程保留时间是60s。线程池拒绝策略为:AbortPolicy。

线程池内部的阻塞队列里面。但是这样有一些弊端,极其容易出现OOM,或者消息丢失。

ThreadPoolTaskExecutor和 ThreadPoolExecutor

  • ThreadPoolExecutor,这个是JDK里面的线程池类,继承自Executor,里面有一个execute()方法,用来执行线程,线程池主要提供了一个线程队列,队列中保存着所有等待状态的线程,避免了创建与销毁的额外开销

  • ThreadPoolTaskExecutor,是spring包下的,是spring为我们提供的线程池类,其异步线程池的接口类是TaskExecutor,本质还是java.util.concurrent.Executor。

ThreadPoolTaskExecutor线程池,有哪几个重要参数,什么时候会创建线程池?

  1. 查看核心线程池是否满,不满就创建一个线程池。满的话执行下一步。

  2. 查看阻塞队列是否满,不满创建阻塞队列。满的话执行下一步。

  3. 查看线程池是否满,即是否达到最大线程池数,不满创建一个线程执行任务,满则按照策略处理无法执行的任务。

高并发下核心线程怎么设置?

  1. 首先我们需要知道是IO密集还是CPU密集?CPU密集设置跟核心数一样大小。IO密集型设置为2倍的CPU核心数。

  2. 但是这个是非固定的,我们还是要根据实际压测结果调整。

考点6:自定义线程池出现的问题

  1. 线程池参数往小了配置(此方法qps小,等待队列小)

  2. 线程池参数往大了配置(此方法qps等待队列大,瞬间很高,但是容易发送OOM)

通过这两个方法,我们会发现一些问题。

  1. 当我们采用异步发送用户体验是变好了,但是数据存在丢失的可能性,阻塞队列存储内存中,如果队列长度过多则重启容易出现丢失数据情况。

  2. 当我们采用异步发送+阻塞队列缓冲,刚开始瞬间QPS高,但是后续还是会降低很多。

考点7:RestTemplate里面存在的问题

服务端向前端socket连接管道写返回数据时 链接(pipe)却断开了。从应用角度分析,这是因为客户端等待返回超时了,主动断开了与服务端连接。连接数设置太小,并发量增加后,造成大量请求排队等待。网络延迟,是否丢包。内存是否足够多支持对应的并发量。

重新认识RestTemplate:

  1. RestTemplate是spring提供的用于访问Rest服务的客户端

  2. 底层通过使用java.net包下创建HTTP请求

  3. 通过使用ClientHttpRequestFactory指定不同的HTTP请求方式,主要提供了两种实现方法。

    1. SimpleClientHttpRequestFactory(默认)底层使用j2se提供的方式,即java.net包提供的方式,创建底层Http请求连接,主要createRequest方法(端点调试),每次都会创建连接会造成极大的资源浪费,而且若连接不能及时释放,会因为无法建立新的连接导致后面的请求阻塞。

    2. HttpComponentsClientHttpRequestFactory底层使用HttpClient访问远程的Http服务。

问题解决:

  1. 客户端每次请求都要和服务端建立新的连接,即三次握手将会非常耗时。

  2. 通过http连接池可以减少连接建立与释放的时间,提升http请求的性能

  3. Spring的restTemplate是对httpclient进行了封装,而httpclient是支持池化机制

  4. 对httpclient进⾏封装的有:Apache的Fluent、es的 restHighLevelClient、spring的restTemplate等

复制代码

@Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate(ClientHttpRequestFactory requestFactory){ return new RestTemplate(requestFactory); } @Bean public ClientHttpRequestFactory httpRequestFactory(){ return new HttpComponentsClientHttpRequestFactory(httpClient()); } @Bean public HttpClient httpClient(){ Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", SSLConnectionSocketFactory.getSocketFactory()) .build(); PoolingHttpClientConnectionManager connectionManager=new PoolingHttpClientConnectionManager(registry); //设置连接池最大是500个连接 connectionManager.setMaxTotal(500); //MaxPerRout是对maxTotal的细分,每个主机的并发最大是300,route指域名 connectionManager.setDefaultMaxPerRoute(300); /** * 只请求 该网站 , 最大并发量300 */ RequestConfig requestConfig=RequestConfig.custom() //返回数据的超时时间 .setSocketTimeout(20000) //连接上服务器的超时时间 .setConnectTimeout(10000) //从连接池中获取连接的超时时间 .setConnectionRequestTimeout(1000) .build(); CloseableHttpClient closeableHttpClient = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig) .setConnectionManager(connectionManager) .build(); return closeableHttpClient; } }

相关推荐
guslegend4 小时前
个人项目复习-云盘Day01
项目面试