这道题比较偏运维,并不是我们开发的职能范围。对于3年以内的开发算是超纲的面试题了,这种题目的回答最好说自己没有权限操作服务器,或者说是组长负责,技术经理负责。强行回答会陷进面试官的坑里
有兴趣可以看这篇文章:https://www.jb51.net/article/133246.htm
Springboot:
个人对于springboot的理解是他对spring框架进行了模块化,将spring和目前使用比较多的技术框架集成配置打包,省了使用者在开发时自己去配置集成。大大简化了spring应用开发过程中的搭建和开发。同时更好的解决了各个框架集成时依赖包的版本冲突以及引用的不稳定性。
核心原理:
1> 基于SpringMVC无配置文件(纯Java)完全注解化+内置tomcat-embed-core实现SpringBoot框架,Main函数启动。
2> SpringBoot核心快速整合第三方框架原理:Maven继承依赖关系。
核心思想:开箱即用和约定优于配置
Springcloud:
springcloud虽然带有‘cloud’,但是它并不是云计算解决方案,而是在springboot基础上构建的,用于快速构建分布式系统的通用模式的工具集
特点:
对比:
SpringBoot专注于快速方便的开发单个个体微服务。
SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、精选决策、分布式会话等集成服务。
SpringBoot可以离开SpringCloud独立开发项目,但是SpringCloud离不开SpringBoot,属于依赖关系。
SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架。
现实中不大的项目使用springcloud只会徒增开发成本,所以在项目前期一般都是使用springboot,更有效率,快捷。
项目构建流程
项目立项
技术选型和项目框架搭建
需求分析
开发阶段
UI界面设计
编码开发
后端开发(整理完需求即可开始接口开发)
前端开发(依赖于UI界面设计和后端接口)
测试(完整的测试由测试==>修改==>回归测试组成)
功能测试
性能测试
上线
redis的使用场景很多,回答是不但需要答出在哪里使用,最好要答出为什么这么使用
具体步骤:
用户进行登录时,提交的登录表单,放入request;
服务器端校验用户名和密码;
通过后将用户信息存储到Redis中,在数据库中的key为session_id;
服务器返回的response中的set-cookie字段包含该session_id,客户端收到后将其值存入浏览器中;
客户端之后的操作的request中都包含session_id,服务器收到后提取出并在Redis中拿到该session,完成业务操作;
使用Redis来实现session的共享和存储,必须要保证session_id,不会被轻易获取和破解,并设置合理的失效时间,对敏感操作必须再次校验用户。
这类数据量大,查询条件比较复杂,性能消耗比较大。重复多次请求会导致数据库压力过大,降低查询效率,甚至宕机
这类数据的特点1是操作次数多,且更新快。redis可以快速的写入,移除和修改,很好的匹配这类数据的特性。
特点2是数据结构,其他缓存的数据结构单一,没有很好能符合这类数据的结构,redis的数据结构丰富,关键是它的hash数据类型很好的符合了这类数据结构的需求
比如,购物车场景,我们首先需要两个HASH来存储,第一个HASH是用户与购物车之间的关系,第二个HASH是购物车中的商品列表。
先通过userId获取到shoppingCartId,然后再通过shoppingCartId就可以获取到用户购物车的ProductIds。然后再通过ProductIds就可以异步读取用户购物车的商品信息。
总体来说redis适用于一些实时性要求不高但是请求次数较多的数据。
Redis的特点(必须了解):
高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。
高并发相关常用的一些指标:
响应时间:系统对请求做出响应的时间。例如系统处理一个HTTP请求需要200ms,这个200ms就是系统的响应时间。
吞吐量:单位时间内处理的请求数量。
QPS:每秒响应请求数。在互联网领域,这个指标和吞吐量区分的没有这么明显。
并发用户数:同时承载正常使用系统功能的用户数量。例如一个即时通讯系统,同时在线量一定程度上代表了系统的并发用户数。
互联网分布式架构设计,提高系统并发能力的方式,方法论上主要有两种:垂直扩展(Scale Up)与水平扩展(Scale Out)。
垂直扩展:提升单机处理能力。垂直扩展的方式又有两种:
(1)增强单机硬件性能,例如:增加CPU核数如32核,升级更好的网卡如万兆,升级更好的硬盘如SSD,扩充硬盘容量如2T,扩充系统内存如128G;
(2)提升单机架构性能,例如:使用Cache来减少IO次数,使用异步来增加单服务吞吐量,使用无锁数据结构来减少响应时间;
不管是提升单机硬件性能,还是提升单机架构性能,都有一个致命的不足:单机性能总是有极限的。所以互联网分布式架构设计高并发终极解决方案还是水平扩展。
水平扩展:只要增加服务器数量,就能线性扩充系统性能。水平扩展对系统架构设计是有要求的,如何在架构各层进行可水平扩展的设计,以及互联网公司架构各层常见的水平扩展实践
我们公司最早是一台服务器2核8Gb,当用户交互开始卡顿时升级到了4核16Gb,后面再次升级成了4核32GB。当用户量上来后,开始使用集群。
这里就是先做垂直扩展,后做水平扩展
项目初期为了快速迭代开发、推荐业务和成本看了,一般选用垂直扩展。当项目起来,用户量上来盈利了,会选用水平扩展来支撑日益复杂的需求
1 定时短信、站内信的发送,涉及上万用户,若单线程的话耗时会很长,多线程能大大缩短发送时间
2 定时操作列表记录(记录之间操作必须独立)
3 导入大批量数据
线上bug一般分为紧急和非紧急:
对于紧急的bug会立即组织测试和开发进行排查,若是代码问题实时修复,修复后测试没问题通知上线,上线后再测试和跟进
对于非紧急的bug会提交到bug管理工具(如禅道)上,统一时间进行排查和修复.修复测试没问题在指定时间进行上线
由于开发人员和测试人员有限,对于bug修复必须分情况和优先级进行修复,毕竟开发和测试有其他任务.
部署多少台服务器根据项目实际情况而定,一般用户在10万以下并不会使用到集群,项目功能简单也不会使用到分布式服务.
公司项目初期为了快速迭代开发一般一个应用即可,这时部署一台服务器即可.从成本和效率上来说是最好的
项目中期的时候,随着功能复杂起来,会对功能模块进行拆分,这时部署会根据模块服务数和用户数来部署
目前我们公司的服务器情况:
商家数8000+,日活商家数1200+,用户数90w+,日活2w+:
PC web端2台服务器,H5 web端3台服务器,后台管理1台服务器,核心服务(处理各种定时任务)部署了一台服务器,上传服务器2台,短信服务器2台,支付服务器2台,图片服务器采用的是阿里云的OSS,数据库采用的是阿里云的RDS-mysql,缓存采用的是阿里云的redis
服务器承受的压力有很多因素:服务器的配置,应用的多少和性能都有关系,我们公司的服务器配置是8和16G,这种配置服务器能承受并发能过2000以上
正常上线必须通过产品和测试,产品需要确定需求中的功能是否已经实现,测试需要确定现有功能是否已经没有功能性问题,只有通过他们的验收项目或者新需求才能上线.
RabbitMQ能承载多少高并发于服务器性能有关,测试4核8G的服务器上RabbitMQ的并发能破1000
这种题目只要说听测试同学说这个样子,毕竟不是所有人都会进行性能测试的.
MySQL 5.0 版本开始支持存储过程。
存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。
存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。
存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。
存储过程就是具有名字的一段代码,用来完成一个特定的功能。
创建的存储过程保存在数据库的数据字典中。
优点
存储过程可封装,并隐藏复杂的商业逻辑。
存储过程可以回传值,并可以接受参数。
存储过程无法使用 SELECT 指令来运行,因为它是子程序,与查看表,数据表或用户定义函数不同。
存储过程可以用在数据检验,强制实行商业逻辑等。
缺点
存储过程,往往定制化于特定的数据库上,因为支持的编程语言不同。当切换到其他厂商的数据库系统时,需要重写原有的存储过程。
存储过程的性能调校与撰写,受限于各种数据库系统。
存储过程总的来说比较鸡肋:
1 受限于数据库和编程语言,导致各个公司使用时有顾虑,之后的迁移和转型成本会很大
2 编写要求不像普通sql方便,没有使用熟悉的编程语言开发效率高
目前我待过的几个项目中均没有使用到存储过程
mysql优化:
目前用到过的:
1 实际开发中禁止使用’select *’
2 当知道结果只有一行数据时使用 LIMIT 1,这样一来,MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查少下一条符合记录的数据。
3 为常用的搜索字段建索引
4永远为每张表设置一个ID做为其主键,而且最好的是一个INT型的(推荐使用UNSIGNED),并设置上自动增加的AUTO_INCREMENT标志。
5 like中’%‘在前面用不到索引,尽可能将’%‘放在后面,进行有模糊查询
6 mysql 不支持函数转换,所以字段前面不能加函数,否则这将用不到索引
7 字段类型转换导致不用索引,如字符串类型的不用引号,数字类型的用引号等,这有可能会用不到索引导致全表扫描
8 当数据量大的时候需要分库分表
网上推荐的还有:
1 在Join表的时候使用相当类型的例,并将其索引:当两个表中Join的字段是被建过索引时,MySQL内部会启动为你优化Join的SQL语句的机制
2 千万不要 ORDER BY RAND(),这样使用只会让你的数据库的性能呈指数级的下降
3 使用 ENUM 而不是 VARCHAR
4 or 的查询尽量用 union 代替(Innodb)
5 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
6 应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描
7应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描
8 in 和 not in 也要慎用,否则会导致全表扫描
当然要分析语句是否要优化一定得会使用Explain关键字,从 PROCEDURE ANALYSE() 取得建议
千、百万级数据处理方法:
当数据达到千百万级别是,查询的效率是个很大的问题,这时写完sql语句都应该使用explain来查看下语句的查询信息,运行下语句看看使用的时间
当效率很低的时候就必须进行优化了:
1 从语句的性能进行优化 -- 以上的优化手段都可以用
2 从表结构进行优化,常见的有
A)对表字段拆分,比如商品表,商品的名称,主图,规格等不变的字段放到基础信息表中,商品的实时信息放到附属表中;
B) 对大表进行按时间拆分表,比如用户表,对指定时间内(60天)没有登录过的用户放到一张用户冻结表,其他的放在用户表,当冻结表中的用户重新登录后再移至用户表中
3 从硬件上进行升级
前面两种是减少语句的扫描量(查询量),后面一种是提高扫描(查询)的性能和效率
1. 选用适合的Oracle优化器
2、重建索引:alter index 索引名 rebuild 【online】
3、强制索引:给该语句加上hint后,强制其使用指定索引
JVM 中最大堆大小有三方面限制:
a.相关操作系统的数据模型(32-bt还是64-bit)限制;
b.系统的可用虚拟内存限制;
c.系统的可用物理内存限制。
32位系统下,一般限制在1.5G~2G;64为操作系统对内存无限制。
常见配置汇总
堆设置
-Xms:初始堆大小
-Xmx:最大堆大小
-Xmn:年轻代大小
-Xss:每个线程的虚拟机栈大小
-XX:NewSize=n:设置年轻代大小
-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
-XX:MaxPermSize=n:设置持久代大小
-XX:MaxTenuringThreshold=n:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代
收集器设置
-XX:+UseSerialGC:设置串行收集器
-XX:+UseParallelGC:设置并行收集器
-XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等
-XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集
-XX:+UseParalledlOldGC:设置并行年老代收集器
-XX:+UseConcMarkSweepGC:设置并发收集器
-XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。
-XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。
-XX:+DisableExplicitGC,这个参数作用是禁止代码中显示调用GC
垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
-XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中断的执行时间
-XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间。
-XX:PrintHeapAtGC:打印GC前后的详细堆栈信息
-Xloggc:filename:与上面几个配合使用,把相关日志信息记录到文件以便分析。
并发收集器设置
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX:+UseParNewGC:设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。
-XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。
-XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片
调优总结
年轻代大小选择
响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。
吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。
年老代大小选择
响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:
并发垃圾收集信息
持久代并发收集次数
传统GC信息
花在年轻代和年老代回收上的时间比例
减少年轻代和年老代花费的时间,一般会提高应用的效率
吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。
较小堆引起的碎片问题
因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:
-XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。
-XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩
经典案例:
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
-Xmx3550m:设置JVM最大可用内存为3550M。
-Xms3550m:设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn2g:设置年轻代大小为2G。整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss128k:设置每个线程的虚拟机栈大小。JDK5.0以后每个线程栈大小为1M,以前每个线程栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
-XX:MaxPermSize=16m:设置持久代大小为16m。
-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
何谓自旋锁?它是为实现保护共享资源而提出一种锁机制。
其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。
自旋锁是一种比较低级的保护数据结构或代码片段的原始方式,这种锁可能存在两个问题:
死锁。试图递归地获得自旋锁必然会引起死锁:递归程序的持有实例在第二个实例循环,以试图获得相同自旋锁时,不会释放此自旋锁。在递归程序中使用自旋锁应遵守下列策略:递归程序决不能在持有自旋锁时调用它自己,也决不能在递归调用时试图获得相同的自旋锁。此外如果一个进程已经将资源锁定,那么,即使其它申请这个资源的进程不停地疯狂“自旋”,也无法获得资源,从而进入死循环。
过多占用cpu资源。如果不加限制,由于申请者一直在循环等待,因此自旋锁在锁定的时候,如果不成功,不会睡眠,会持续的尝试,单cpu的时候自旋锁会让其它process动不了. 因此,一般自旋锁实现会有一个参数限定最多持续尝试次数. 超出后, 自旋锁放弃当前time slice. 等下一次机会。
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。
目前一般会使用redis来实现分布式锁,核心代码:
单点登录(Single Sign On),简称为 SSO。是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
有一个独立的认证中心,只有认证中心才能接受用户的用户名和密码等信息进行认证,其他系统不提供登录入口,只接受认证中心的间接授权。间接授权通过令牌实现,当用户提供的用户名和密码通过认证中心认证后,认证中心会创建授权令牌,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌即得到了授权,然后创建局部会话
特点: 一处登录,处处穿梭
1 请求的耗时
2 请求的个数
3 查询的个数
4 查询的耗时
5 代码运行的耗时
6 代码的性能
7 代码的耦合性
8 代码的可读性
总之:优化一定得从以下几个方面考虑
性能:如代码的耗时,请求响应速度,请求的个数,查询的个数,查询的耗时
可维护性:如代码的耦合性,可读性,可维护性
常见的方式:
1 图形验证码,这常用于短信接口的防护
2 限定请求次数,常见于秒杀,抢购等功能中
3 流程条件限制,如满足一定条件,注册用户,获取权限等
4 ip地址限定,java中一般写拦截器对统一ip进行判断,同一ip指定时间内只能访问指定次数
5 服务器接口验证:常见于一些需要付费的接口,这类接口需要内部的token或秘钥才能请求
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据(Dirty Data),依据脏数据所做的操作可能是不正确的。
扩展:不可重复读是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读
脏数据在涉及到历史数据,资金等重要数据几乎是致命的。
比如:在秒杀系统中,脏数据的产生可能会导致库存不足,商家就会亏损;在资金系统中,若有脏数据会导致整个系统统账错误。
上线的项目查看bug一般是通过日志系统。所以项目中必须有详细日志记录,在更新数据前后,重要操作节点都应该有详细的日志记录。
项目中异常的处理无非是两种方式:
1 try catch 抓取异常
2 throw 抛出异常
方式简单,但是注意的细节却不少:
1. 能通过预检查方式规避的RuntimeException不应该通过catch方式处理,如IndexOutofBoundsException(角标越位异常),NullPointException(空指针异常)
2. 异常不能用来做流程控制或条件控制:异常的效率远低于比较判断方式
3. try catch时不能一股脑的吧代码全包含进去,这只会增加问题定位的难度,使得代码无法根据不同的异常做出处理,同时也是不负责任的表现
4. 捕捉异常的目的是处理他,若不处理就应该将异常抛给他的调用者。在展示给用户之前必须处理掉
5. try catch中若有事务代码,在catch完后一定注意是否需要rollback事务
6. finally代码块中对资源对象、流对象进行关闭时若有异常也要做try catch处理
7. 捕获异常与抛出的异常类型要完全匹配,或者捕获的异常是抛出异常的父类
8. finally代码块中不能有return:当有return时就不会执行try catch中的return
目前线上的日志使用的是log4j,测试时会记录debug级别的信息,上线时记录info级别的信息。一般更新数据库数据前后需要打日志,敏感操作节点也需要打日志.这些日志都会写到日志文件中保存起来,一般的日志文件保存7到15天.
跟踪线上问题:
1 先确定问题是在哪个页面或者哪个功能中,然后查看功能对应的日志文件,看看是否有Error信息或者Exception信息
2 若没有异常信息说明很可能是代码逻辑的问题,查看功能日志点看看日志情况,一般是能定位问题点
3 若从日志中定位不出来只能是复盘功能代码
日志的存储:
单服务器上日志一般存在指定的目录下,可在日志配置中定义
分布式/集群:可以存在各种服务器上,也可以使用日志服务器统一管理日志文件
测试的职责是找出项目应用中存在的问题,及时提交给开发修复.在上线前将问题尽可能的找出处理掉.现在很多开发觉得测试和他们是对立面,经常和测试针锋相对,推脱问题.其实测试和开发都是为了将项目功能尽可能的完善,交付给用户使用,这是他们的共同目的.
和测试的交互应该注重客观事实,根据测试提交的问题排查是什么原因造成的,然后有问题的解决掉,没问题的反馈给测试重新测试.重点是和测试多沟通,了解彼此的想法,不能造成误解,影响工作.
至少我现在公司开发和测试很和谐,每天测试工作时遇到需要紧急需马上解决的问题会马上和开发沟通,是否真正有问题由开发排查后反馈解决.非紧急的问题会提交到禅道上,由开发自己去看和解决自己的问题.测试每天定点看禅道的bug解决率,并提醒开发前去解决bug
java项目部署目前一般部署在tomcat上,springboot出现后直接将项目打成jar包进运行。
部署前需要对环境进行确认:确认项目应用所需的环境是否正确,如系统环境,java版本,数据库版本,系统的硬件配置
对项目的配置文件进行确认:有些项目的配置文件在测试时需要修改,这时上线的时候需要改回去,如果没有check,可能会使用测试配置上线,这样就好导致上线出现问题
对数据库表字段进行确认:防止因为新增的表或字段未添加或名称有出入导致上线后报错
对上线时间的确认和通知
上线时对以前线上的代码进行备份:确保上线失败时可以回退
上线后:必须对所上线的功能进行再次测试,防止有问题
问题的反馈: 测试测试出来或者用户反馈过来
问题的确定: 开发拿到问题后现在测试服中重现问题,无法重现的通过查看日志,复盘代码来定位问题
问题的解决: 一般小问题会在主干上修复并测试上线,大问题会开分支专门修复问题测试没问题后合并再测试上线
通知: 问题解决上线后通知相关的部门和人员修复结果(做到事事有结果)
1 未做分布式锁时经常会出现并发和重复请求的数据:添加分布式锁
2 秒杀抢购被用户摸到接口,使用工具重复提交抢购:使用拦截器对同一ip同一接口进行请求间隔限制
3 一个用户多地登录导致数据异常:限制用户只能一地登录
4 数据库被清空:好在数据库数据每小时都有做备份,吧备份数据导回线上数据库,但还是损失了备份点之后的数据
5 系统达到瓶颈:升级硬件配置,使用集群
目前我碰到的有两种实现方式:
1 用户购买一件商品就生成一条订单记录,商品的库存减一
2 在商品发布时生成库存量个token,每次用户购买商品就获取一个token,同时生成一条订单记录,token使用完后失效
第一种需要注意重复提交和并发问题
第二种适用于商品库存量不大的应用
微信和支付宝支付接口的对接都有相应的流程,他们都提供了详细的对接demo.这两种支付方式以及快钱支付方式的对接都基本一致.
原理:
用户发起支付请求,应用服务端将支付的金额,支付的原因以及应用生成的唯一商户订单号使用支付接口方提供的加签方式进行加签后,生产预支付记录,同事调用支付接口进行支付.支付提交后有两种获取回执相应的方式:同步和异步.同步是用户点击支付完成,由服务端像支付接口方服务器发起查询,对查询结果进行校验操作.异步是支付接口方接受到支付打款后向我们应用指定接口发生支付成功的请求,我们对请求进行解析校验操作.无论异步还是同步都会返回我们生成的唯一商户订单号作为回执.操作成功后,根据商户订单进行更新记录
安全性: 这几种支付方式在提交支付是都会有加签步,当对应支付完成会有他们服务器回调我们接口.这时我们对他们的请求参数用指定的密钥和解签方式进行解签,对解签结果进行校验.
对购物车里的商品加锁,只有拿到锁的一方才能进行下单处理,另一方无法下单操作.
现在我们处理的方法:
1加密,一般是对称加密的方法.
2 信息使用特殊字符替换,如手机号:在服务端将手机号中间几位使用*替换掉