发表于: 2021-02-22 23:54:42

1 1223


今天完成的事情:

列出来每一个请求的时耗分解图

学习spring-AOP,根据教程用aop记录controller的出参入参

深度思考


明天计划的事情:

代码改善,深度思考。spring-aop日志


遇到的问题:

以下



收获:

请求分耗:

方法名称所耗时间MS
insertBanner139
showBanner116
postContents27
selectMessageByWorkId79
insertStudio221
getFirst159
Work131
insertWork101


spring-AOP基础概念

1.面写切面编程

2.预编译+动态代理(程序运行期间)

作用:

在程序运行期间,不修改源码对已有方法进行增强。

 

AOP 相关术语

 

Joinpoint(连接点):

所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点。


Pointcut(切入点):

所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。


Advice(通知/增强):

所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。

通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。


Introduction(引介):

引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方

法或 Field。


Target(目标对象):

代理的目标对象。


Weaving(织入):

是指把增强应用到目标对象来创建新的代理对象的过程。

spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。


Proxy(代理):

一个类被 AOP 织入增强后,就产生一个结果代理类。


Aspect(切面):

是切入点和通知(引介)的结合。

 

AOP为什么会出现?解决了什么问题?

为了减少重复代码,将和业务代码无关的模块都分开,比如:权限,日志,事务


根据教程用aop记录controller的出参入参。

定义一个切入点:

/**
* 定义一个切入点
*/
@Pointcut("execution(* com.kbk.controller.*Controller.*(..))")
private void logRecordArg(){};

使用环绕通知:

/**
* 定义一个环绕通知记录controller出参入参
*/
@Around("logRecordArg()")
private Object getControllerLog(ProceedingJoinPoint point) {
//获取目标方法所在类的全限定名。
   String methodName=point.getSignature().getDeclaringType().getName();
   StringBuilder sb = null ;
   sb = new StringBuilder(300);
   // 类名+方法名
   String target = point.getSignature().getDeclaringTypeName() + "." + point.getSignature().getName();
   Object[] args = point.getArgs();
   String[] paramsName = ((MethodSignature) point.getSignature()).getParameterNames();

   sb.append(target).append(" 入参:");
   if(args != null && paramsName != null && args.length > 0 && paramsName.length > 0) {
for (int i=0; i< paramsName.length; i++) {
sb.append(" ").append(paramsName[i]).append(" = ").append(args[i]).append(",");
       }
sb.deleteCharAt(sb.length()-1);
   }
sb.append("");

   Object result = null;
   try {
result = point.proceed();
   } catch (Throwable throwable) {
throwable.printStackTrace();
       logger.error("错误信息:"+throwable.getMessage());
   }
sb.append(" 出参:").append(result).append("");
   // 记录日志
   logger.info(sb.toString());
   // 调用结果返回
   return result;

}

结果:

===============================

===============================

1.nginx服务器有什么作用?什么叫反向代理?为什么要使用反向代理?

反向代理和负载均衡。

反向代理就是nginx充当tomcat,jetty,resin等等要具体运行项目的服务器的中介。

作用:

防止主服务器被恶意攻击

为负载均衡和动静分离提供实现支持

负载均衡:使用反向代理同时代理多个相同内容的应用服务器(比如tomcat),将客户端请求分发到各个应用服务器上并接收响应返回给客户端。

2.什么是代码生成,mybatis generator代码生成是怎么实现的,还有什么办法可以生成代码? 

Mybatis generator 生成的文件分两大块,共四种:实体(实体类 pojo.java、条件查询类 pojoExample.java)映射(映射文件 pojoMapper.xml、接口 pojoMapper.java)

MBG 连接数据库后会扫描指定的数据库与表,并且得到他们的所有字段、类型、长度、主键信息来建立实体类。

其中字段名会按照配置文件的规则进行格式化,MBG 中有一个类型映射表,根据 mysql 中的类型及长度信息转换为 java 的类型。

其中会遇到一些问题,对于 mysql 中的  int tinyint bigint 来说,长度信息并不会影响他们的存储长度,只会在开启自动填充零的时候对其显示效果产生影响。但是在 MBG 的默认映射表中,tinyint(1) 会转换为 java 中的 boolean。这个时候我们可以通过修改配置文件来改变映射。

 

在 example 类中,会生成很多条件查询、排序的方法,主要的作用是对 sql 进行拼接,内部的方法非常多,有针对每一个字段的 or and between noteuals eqals 方法,使用的时候可以根据需求进行自由拼接。

另外还可以设置排序方式,当然这部分就需要用户来自定义一段 sql 语句了。

 

其他的自动生成代码方法:

Velocity 模板引擎快速生成代码

https://www.ibm.com/developerworks/cn/java/j-lo-velocity1/index.html

FreeMarker

http://freemarker.foofun.cn/index.html


3.Mysql的一般而言应该配置多大的内存, 多大的硬盘 ,多大的连接数? 

这就涉及到了MySQL的优化了了吧。

一般来说 Mysql   的内存设置应该尽量接近物理机的内存,但又要给其他的服务与系统留下足够的内存空间。

内存和硬盘相比微不足道


硬盘,机械硬盘和固态硬盘又不一样,

这要取决于磁盘,如果你使用的是 SSD 固态硬盘,它不需要寻址,也不需要旋转碟片。打住打住!!!你千万可别理所当然的认为:“既然SSD速度更快,我们把线程数的大小设置的大些吧!!”


结论正好相反!无需寻址和没有旋回耗时的确意味着更少的阻塞,所以更少的线程(更接近于CPU核心数)会发挥出更高的性能。只有当阻塞密集时,更多的线程数才能发挥出更好的性能。


公理:你需要一个小连接池,和一个充满了等待连接的线程的队列


实际上,连接池的大小的设置还是要结合实际的业务场景来说事。

比如说,你的系统同时混合了长事务和短事务,这时,根据上面的公式来计算就很难办了。正确的做法应该是创建两个连接池,一个服务于长事务,一个服务于"实时"查询,也就是短事务。


还有一种情况,比方说一个系统执行一个任务队列,业务上要求同一时间内只允许执行一定数量的任务,这时,我们就应该让并发任务数去适配连接池连接数,而不是连接数大小去适配并发任务数。


参考网址:https://www.jianshu.com/p/1c9ffde4c704



4.在端到端的请求当中,建立Http连接需要多久,Model通过JSP转成Json需要多久,Nginx调用Resin需要多久,Service访问DB需要多久,一个Sql语句执行的时间是多久。 

找到了这个,明天试试

➜  ~ curl -w "@curl-format.txt" -o /dev/null -s -L "http://cizixs.com"

    time_namelookup:  0.012

       time_connect:  0.227

    time_appconnect:  0.000

      time_redirect:  0.000

   time_pretransfer:  0.227

 time_starttransfer:  0.443

                    ----------

         time_total:  0.867

可以看到这次请求各个步骤的时间都打印出来了,每个数字的单位都是秒(seconds),这样可以分析哪一步比较耗时,方便定位问题。这个命令各个参数的意义:

-w:从文件中读取要打印信息的格式

-o /dev/null:把响应的内容丢弃,因为我们这里并不关心它,只关心请求的耗时情况

-s:不要打印进度条

从这个输出,我们可以算出各个步骤的时间:

DNS 查询:12ms

TCP 连接时间:pretransfter(227) - namelookup(12) = 215ms

服务器处理时间:starttransfter(443) - pretransfer(227) = 216ms

内容传输时间:total(867) - starttransfer(443) = 424ms

参考网址:https://www.ruanyifeng.com/blog/2019/09/curl-reference.html

https://cizixs.com/2017/04/11/use-curl-to-analyze-request/



5.什么是Sql注入,应该怎么解决?对于未做SQL注入防范的程序,你可以直接通过调用接口删掉表吗? 

  SQL注入就是一些攻击者把SQL命令插入到web表单的输入或者页面的url中,欺骗服务器执行恶意的SQL语句。在某些表单中,用户输入的内容直接用来构造或者影响动态SQL命令,或者作为存储过程的输入参数。这类表单特别容易收到SQL注入式的攻击。

   解决方法:

1)采用预编译语句集,它内置了处理SQL注入的能力,只要使用它的setXXX方法传值即可。

2)使用正则表达式过滤传入的参数.

3)字符串过滤


SQL注入攻击实例

比如在一个登录界面,要求输入用户名和密码:

可以这样输入实现免帐号登录:

用户名: ‘or 1 = 1 –

密 码:

点登陆,如若没有做特殊处理,那么这个非法用户就很得意的登陆进去了.(当然现在的有些语言的数据库API已经处理了这些问题)

这是为什么呢? 下面我们分析一下:

从理论上说,后台认证程序中会有如下的SQL语句:

String sql = "select * from user_table where username=

' "+userName+" ' and password=' "+password+" '";

当输入了上面的用户名和密码,上面的SQL语句变成:

SELECT * FROM user_table WHERE username=

'’or 1 = 1 -- and password='’

分析SQL语句:

条件后面username=”or 1=1 用户名等于 ” 或1=1 那么这个条件一定会成功;

然后后面加两个-,这意味着注释,它将后面的语句注释,让他们不起作用,这样语句永远都能正确执行,用户轻易骗过系统,获取合法身份。

这还是比较温柔的,如果是执行

SELECT * FROM user_table WHERE

username='' ;DROP DATABASE (DB Name) --' and password=''

….其后果可想而知…



6.在内存里拼装数据会节省时间吗?如果不能,为什么要选择单表查询,而不是直接拼装成Sql语句。

内存拼装数据是啥意思,单表查询速度快



7.为什么一般而言,不允许使用连表查询,不允许使用复杂的Group By等语句,为什么不允许使用存储过程? 

优点:

a,简化了代码逻辑

b,减少了不必要的数据传输,毕竟查询完很多数据都是不需要的

 

缺点:

a,数据表如果由修改那么连表查询就需要做修改,工作量未知

b,连表查询会锁表,不利于写操作

c,sql 中可能会有复杂的嵌套,写完就忘




8.为什么响应时间一般不允许超过200MS,怎么查看一个请求从发起到结束,耗费在什么地方了?

超过200ms会影响用户体验,通过配置nginx的conf.xml文件,使用aop进行日志记录,记录controller和service的执行时间。

主要耗费在网络延时,数据库连接这几块地方。  



9.为什么要自测,仅仅使用Postman来测试足够吗?什么是本地测试,什么是在开发环境测试?在开发过程中,应该每天部署代码到开发环境吗,为什么? 

自测是因为自己需要对写出来的程序运行情况的一个了解,哪个地方报错,哪个地方需要调优都要了解到。仅仅使用postman测试不够,因为不知道细节方面的耗时,比如连接db的时间,service的运行时间,controller的运行时都不能了解到。

本地测试就是在ide写完代码之后部署到本地tomcate等服务器进行测试。

开发环境(DEV):开发环境是程序猿们专门用于开发的服务器,配置可以比较随意, 为了开发调试方便,一般打开全部错误报告。

测试环境(UAT):一般是克隆一份生产环境的配置,一个程序在测试环境工作不正常,那么肯定不能把它发布到生产机上。

生产环境(PROD):是指正式提供对外服务的,一般会关掉错误报告,打开错误日志。

需要,因为要持续集成,改完代码立即提交。 



10.保存图片有几种方式?什么样的情景下应该使用哪一种? 

保存在数据库或者保存在硬盘

如果图片比较小,可以选择二进制直接存储在数据库中

如果过大就要保存在硬盘中,数据库对应的字段是链接或者服务器相对路径








返回列表 返回列表
评论

    分享到