面试串讲5:系统架构及项目设计
1、Cookie操作
cookie是不是必须客户端跳转完成之后才会被设置进去?如果我用response.setCookie后有服务器跳转,在服务器跳转后我去取这个cookie是不是就取不到啊?在显示页显示不了,但是我刷新一下就能找到cookie了。
对于Cookie的设置,有两种模式:
WEB服务器自动设置:指的是你第一次访问里面的任何一个页面;
手工的方式来设置Cookie;
大部分开发者都会忽略一个关键性的问题,那么就是Cookie的路径问题。在默认情况下假设说你现在在“pages/back/msg”目录下,你在这个目录下执行了response.addCookie(c),那么这个时候的cookie是会存在此目录下。那么这个时候即使设置了Cookie,如果你更改了目录,例如:“/pages/back”下,那么也无法取到Cookie,因为路径不同,所以就需要在设置Cookie的时候,如果确定要保存,除了设置时间之外,也需要设置路径,路径就设置为根目录“request.getContextPath().”。
可以取到,关键看cookie设置的路径。跳转也有两种形式,服务器端跳转和客户端跳转,如果用服务器端跳转,第一次肯定取不到,必须在客户端刷新一下再次请求,才可以把cookie刷过来。
2、mvc设计
mvc模式中,各个层级的职能划分,哪些功能写在那块?
所谓的mvc设计模式实际上是一种思想,虽然从整体上来说是划分为了三层,但是实际上里面会划分许多的层。
比如有一个业务的完成,要分很多步,我们是把这些步骤放在服务层还是Servlet中?
服务层是可以单独抽取的,也就是说如果你的一个项目要充分的考虑到可扩展性,那么绝对需要将业务层单独抽取出来实现RPC调用(WebService)。
一定要清楚的是控制层不负责任何与数据库的操作,是任何的操作都不负责,所以控制层只会存在有一个功能:调用业务层,业务的方法只有一个,如果业务操作要返回多个内容,那么就使用Map集合返回。
如果放在Servlet中会准确的把各个步骤的错进行捕获并处理,但这样会显得控制层很重,如果放在服务层的话Servlet又不能明确的对各个步骤失败进行处理了。
控制层一定要处理所有的错误操作,如果不处理,你可以直接选择一个抛出,而后在整个的web.xml文件里面配置一个错误页,这个错误页只要是产生了xxx异常,那么就自动跳转到一个错误页上显示。
但是我个人的习惯是自己手工来进行处理,因为以上的跳转属于全局模式,也就是说不管出现什么样的错误,都跳转到一个页面上进行显示,这样的做法会造成错误的信息不明确的问题。
各个层使用的技术
只有把单机的MVC彻底弄清楚之后才可能牵扯到更多的实际的开发问题,而集群的开发也只是一种扩展而已。
3、MyEclipse开发项目
MyEclipse现在比较悲催的一点就是它只能开发一些低版本程序了。
从MyEclipse2015开始,必须现有WEB项目,而后才可以建立WEB Server。
使用MyEclipse运行项目的时候一定要在网址后面加上项目名称。
4、SSO处理流程
所谓的单点登录用在什么场景上?
CAS有一个自己的流程。
就是CAS服务器的配置,因为版本的区别很大,所以强烈建议大家使用稳定版本开发。同时这里面还需要针对源代码
进行大量的修改与配置才可以得来。
5、读、写分离性能
数据库的读、写分离。此种考虑需要根据你的实际业务要求,不能凭空设计。
在讲解Oracle的时候说过这样一个简单的架构:
信息的汇总表,同时为了保证查询的性能,需要增加索引,但是这张表平均每1秒要更新1000次,但是这样就和索引产生了冲突,所以定义两张表,一张表作为更新使用,另外一张表在系统安静下来的时候进行差异的备份,而后进行数据的保存。
以上是在WEB2.0以前的概念,从WEB2.0开始(AJAX开始)数据量开始暴增,于是你的老板可能就要求你需要对系统进行大规模升级:要求保证更新速度,要求保证实时性,要求保证数据的有效性。
如果这个时候所有的设计还是围绕着传统的关系型数据库展开,你的设计一定是失败的。
6、分布式锁实现
所谓的分布式锁指的是在高并发访问的情况下使用的一种技术,所谓的高并发访问指的就是多个线程对象(每一个操作用户)为了保证资源的操作完整性而实现的一种技术,这样的技术可以简单的理解为锁。
如果现在多个线程在同一个虚拟机之中,正常编写一个程序。而后这个程序里面产生了若干个线程,并且这些线程要操作统一资源。那么在这样的情况下,为了保证资源操作的同步最简单的处理模式就是采用synchronized关键字来完成。
但是这样的做法只适合于单JVM运行情况下,而如果说现在划分到网络上。
但是如果是多虚拟机的状态下,这样的设计就必须做出更改了。
7、负载均衡设计
对于项目的开发需要考虑几个核心问题:高可用(主备关系)、高并发(可以承受大并发用户访问)、分布式。真正做到这个层次架构的时候,你的系统平均每天的访问量不可能低于500W-1000W用户,如果没有到这个用户的量级,那么可以肯定的是用这样的架构会造成大量的额外硬件成本(云服务器(云服务器的出现解决了企业搭建私有云问题))。
在正规的项目开发过程之中肯定会有专业的前端开发者,这类人员会使用ES6.0标准进行前端开发,例如JQuery、Vue.JS、React等,所以这部分的设计不做考虑。
在整体的设计过程之中,就必须去考虑性能的均衡,当然服务器的选择和你要使用的技术的选择也需要根据实际情况确定。要根据你并发访问人数来决定你的技术架构。
8、复杂度
评价一个项目的好与坏,有一个最简单的标准:时间复杂度和空间复杂度。
时间复杂度指的是你的处理逻辑非常的复杂,例如:递归、循环结构复杂,复杂度直接的影响就是你的CPU的占用率高。
空间复杂度:你占用的内存大,例如:在进行JDBC进行数据查询的时候实际上会出现一个问题:如果假设你的数据库有1000w条数据,没有使用分页,那么这些记录都将加载到内存之中。所以你的内存占用率就会很高,假设这些数据会占用20G的内存,那你的一个服务器一定需要为上万人服务,那么这个时候所占用的内存就会非常的庞大,你的服务器根本就无法处理。
对于空间复杂度的操作处理,是需要通过最简单的分页算法可以实现约定,而对于时间复杂度,如果太复杂的逻辑运算,往往不会在一台服务器上运行,需要设计多台的开发服务器。(不是现在可以理解的,只是3~4年工作经验)
最好的效果:处理块,占用内存小,你的程序一定要写到位。
9、红绿灯系统设计
如果你的面试公司给了你这样的一个问题,那么考虑的只是一个逻辑分析能力。因为这种开发的操作从现实来看都是通过硬件模拟的,那么如果非要通过软件模拟,就需要准备好可能使用到的技术:
java编写:Graphics类进行绘制开发:
WEB编写:HTML5中提供的Canvas进行编写
面对此类的问题一定要有一个假设前提:
是否需要有黄灯缓冲,缓冲的变更时间?
是否需要智能调整,如果发现车流量较大,则适当延迟通过时间;
对于违规的车辆的监控情况。
还可能考虑转向灯的设计
实现整个操作的技术环节:
定时器:Timer、TimerTask,但是这两个类是需要时钟支持,可是不准,如果要准确则需要QuartZ这个组件
描述所有的灯的变化,一定要有一个线程的同步处理机制、synchronized、使用单例实现;
既然有两组灯,就建议设计一个单独红绿灯类,这个类可以使用一些参数变化完成,例如:
|-控制变量 =0:表示红灯;
|-控制变量 =1:表示绿灯;
|-控制变量 =2:表示转向灯;
|-控制变量 =3:表示黄灯(绿灯变为转向);
|-控制变量 =4:表示黄灯(转向变为红灯);
如果你现在只是希望给出一组状态,实际上就可以设置一个以下几位:
111,可以描述七个值。
如果要编写还需要考虑传感器问题:包括监控传感器、流量传感器。
既然已经有了各种传感器,那么就可以再设置几个传感器:车速传感器,可以进行大数据的汇总,计算平均的车速,好为城市的交通规划作出数据的贡献。
开发流程:
1、需要先实现定时进行灯的切换处理,如果你需要程序编写,如果使用无界面编写,这个输出的信息非常麻烦;
2、需要考虑监控的问题,如果只是在软件上模拟,可以设置几个坐标点,而真实的环境需要有传感器
3、考虑数据分析问题,可以对相应数据进行采集与汇总。
10、开发团队组成
1、项目中的人员安排
任何一个技术型的公司里面都可能有若干个开发团队,每个团队的开发人数基本上就在3~6个人之间,这样基本上每个团队都有一个架构师(写代码),而且每一个团队对应的是一个具体的业务。在团队里面3个开发,以及一个美工,这些是正规的开发团队的组成,但是所有的团队之上都会有项目经理存在。
也有一部分的公司全部请的都是架构师,这些架构师自己直接实现代码,这一类的人群技术的要求是比较高的,但是在整个的项目公司里面还会有一些辅助人员:系统测试(多个组有两个,每个组有一个),支撑的人员(打杂),这个就属于项目的维护人员。
所以如果你要想从事软件行业,那么最好的做法是不去做这些辅助的工作,而直接上手开发,这样对于你日后的发展是非常有帮助的,从最初的强调全栈工程师,到现在强调的是开发+运维,那么以后全员架构时代,也就是说所有的开发人员一定都是能独挡一面的高手,这类人的工资虽然高,但是整个企业运行来讲,这样的成本是最低的。
在以后的开发之路,懂架构有未来,两年之后,这就是技术人员的基础要求了。
11、密码加密处理(密码加盐)
所谓的加密往往一般都会存在有一种解密程序。毒蛇出入七步之内必有解药。
最简单的加密,
你的原始密码:abc; 加密:cba;
那么说如果所有人都知道有这样的密码结构,那么就可以很轻松的破解,可是如果你在里面追加一些内容;
你的原始密码:abc; 盐值:_; 新密码:c_b_a_;
如果按照原始规则,那么现在就无法得到正确的原始密码。
也就是说盐值是让整个的密码看起来更加安全
MD5的结构特征是不可逆,但是使用时间长了有些人就开始找到了一些规律,那么为了不让这些人破解我的密码,那么就在生成密码的时候增加一些额外的内容,这样的内容就是盐值,这样就可以避免这些人来破坏,不同的项目使用不同的盐值来进行处理。
Shiro里面盐值:password+({Base64的加密程序}),密码才保存的更加安全。
12、商品秒杀设计方案
如果要进行商品秒杀的操作一定要有一个前提:预估数据量。
小米进行抢购的时候都需要针对数据量进行预估:所有的人需要报名参加抢购;
京东或淘宝的时候发现缺少报名,它们是依靠大数据分析系统得来的预估数据量;
如果没有预估数据量,那么整个的系统的先期准备就会不足。现在假设如果要进行秒杀操作
用户进行秒杀的登记;
时间一到开始进行秒杀操作;
在秒杀的过程之中需要出现一个等待界面,如果此类界面刷新了,则抢购失败;
13、日志输出
在一般的系统开发里面,对于一些调试的数据都很少去使用System.out进行输出,几乎都会配置Log4j开发包,这个开发并不复杂,你只需要配置上开发包,而后使用Logger类就可以使用了。
里面分为几种级别:info()、error()、warning().如果出现了异常一定使用的是error()。
之所以使用log4j输出,主要是方便进行一些调整。日志是我们解决问题的关键,也就是说所有的日志都应该输出到一个指定的目录之中,但是这样的配置都是固定的,不需要做特别的处理。
需要的就是配置log4j、slf4j这样的开发包就可以使用日志输出了。
14、企业项目部署
实际的环境的架构设计,必须要充分的考虑到你的业务需求以及所谓的高峰访问的情况,假如说你有一个系统一年就10个人访问,那么就不需要去搞架构了。
一般如果要进行架构设计,必须考虑一下几点:
该架构能否动态扩容;
该架构能否支持HA机制;
以上的集群设计是为了考虑性能平衡,但是会有一个问题存在:没有考虑到HA机制,如果考虑到高可用机制还需要追加更多的协助主机,这些主机将作为备选使用。(如果说企业架构的话,这样的一套集群涵盖了大部分的企业互联网开发的企业操作情况,做到了一定程度需要深度学习)
15、实际项目开发场景
项目的开发之中有多少个数据库?
在实际的项目开发过程之中,只有一点是确定的,那么就是Tomcat里面是没有数据库的。
如果要进行实际的项目开发,往往需要要有许多的子系统,所以现在的开发领域上经常出现一个概念:微架构。其中这种微架构的设计使用两种开发技术:Dubbo、SpringCloud.
如果要是将项目进行子系统的规划设计,所有的子系统里面包含的就是所有的业务层接口以及数据层的接口。
如果你是做的一些基础开发,那么对于整个的开发技术而言,你只需要一个数据库实现数据即可,当然这里面还需要考虑库表分离的问题,所有的数据库不可能无限制的让数据增长。
16、数据库优化
数据库本身是存储结构数据的,所以所谓的数据库优化都是指的是传统的关系型数据库操作。那么对于数据库的优化有以下的几个使用原则:
你需要有一个非常专业的DBA,可以根据你的服务器的配置调整你的服务器的配置调整你的数据库的运行环境;
数据库需要选择合适的操作系统才可以返回优势,例如:DB2只能在AIX下运行;
请保证你的查询语句不会写的特别荒唐(例如:你大量的采用了多表查询,并且在高并发的情况下依然采用同样的方式进行);
可以将部分的数据静态化到缓存之中,例如:城市、省份的信息、学校、住宅楼的信息基本不会发生变化;
如果以上的要求都做到了,数据库的操作依然很慢,那么就有可能是数据量太大的原因了,那么此时你再怎么进行优化,你的数据库的操作也不可能得到质的提升,那么这个时候就必须做先期的项目预估,这个预估的时候就需要考虑进行进行库表分离的有效设计:
数据库的分片保存(数据备份问题,一主多从的备份);
数据的读写分离(你可以使用多个数据库同时完成数据的读取的负载均衡);
如果从程序本身的角度来讲,每一个用户的请求一定要及时的关闭好数据库的连接,不要打开过多的无效链接及在你的项目之中应该配置上数据源。
17、系统设计
我自己的个人理解:
1、你需要清楚你所进行开发项目的行业背景;
|-如果在进行一个系统的设计之初进行了大量的分析与设计,结果最终做完成品设计与你的初期设计有很大的差别。那么就建议不要做特别具体的规划,可以让甲方派人驻扎,这样可以随时进行业务沟通,随时进行设计;
2、在整个系统的设计之中,如果确实有必要会考虑将一些数据进行冗余处理;(不决定,根据系统决定)
3、在系统设计之中你必须进行并发访问的预估,非高峰时期的访问量、高峰时期的访问量;
4、你的系统需要使用那些额外的第三方接口才可以更好的完成功能,例如:支付宝;
5、你还需要考虑你项目的子系统设计问题,因为必须要考虑某一个系统的损坏,不会影响其他的系统;
|-如果要考虑到子系统的问题,那么就绝对不可能只使用一个服务器开发了,你往往需要的是一个服务器的集群,同时建议子系统的设计采用Restful架构完成;
6、还需要考虑与其他平台的交互问题;
7、考虑数据的安全问题;
实际上现在对于开发人员有些尴尬:我们开发人员现在的定位好像不这么清楚,在许多非专业的人士看来,只要跟电脑有关的一切都应该交给开发技术人员完成。如果真的在工作之中进行架构的设计,往往应该有一些专业的搞服务器运维的人士来进行。
这些内容与你学习的开发代码只有一张纸的距离。(但是这层膜要一两年的经验才能破)
18、系统重构
系统重构这一个概念实际上会发现经常出现在我们的开发环境中,我第一次听到系统重构的概念是在2004年的时候听到的,当时我个人的第一反应:为什么要重构?
对于现在的技术给出的网上资料其实特别的多,但是在当初的时代能够获得技术的来源只有图书,所以我们都会翻阅图书进行查看,但是对于当时只有一些纯粹的理论概念上零散的讲了点所谓的重构知识。
【个人意见】我仅仅是以我个人意见进行说明:
系统重构前提:
|-你现在的系统已经无法满足用户的使用要求,也就是说在白天的时候该系统由于办公的人数较多,访问量也高,所以系统的负荷很大;
|-你现在业务的流程出现了改变。
重构的模式:
|-业务的重新梳理,也就是说你需要根据现在已有的业务实现进行升级的逻辑改造,这一改造就会牵扯到数据库的设计变更,同时这个变更还需要保留好原始的操作数据
|-将一个服务器上运行的项目,拆分到多个服务器上运行,这样可以有效的实现负载均衡;
|-需要将你的业务以子系统的形式出现,也就是说一个综合的系统之中需要拆分出无数个子系统进行共同的支撑,同时还需要准备出若干种RPC方案技术(Dubbo、Rest-微架构);
|-数据的备份存储问题,因为访问量大就有可能不是一台数据库可以支撑的;
|-你可能还需要准备出多个WEB端,那么这些WEB端的数据的共享,那么就需要准备出Redis(Redis-Cluster),这些WEB端需要同时被nginx或apache做反向代理。
【企业内部】这样重构的好处:
所有的业务子系统独立出去之后可以进行各种系统间的整合处理(各个子系统之间不要互相调用);
适合高并发操作访问,至少保证速度不会慢;
数据的操作都基于Redis缓存处理;
缺点;服务器的成本加大,因为如果要做高可用的配置的话,基本上还需要增加至少10台服务器的成本。
软件设计没有“1+1=2”这样的公式,我们唯一可以做到的事情就是根据具体的业务进行分析。出发点:高可用,高并发,分布式。
19、项目阐述
1、项目实际上在我们看来没有大小之分。有的只是你的业务逻辑是否清楚。
在你进行项目设计的时候应该更清楚这个项目的设计的业务,可以对某一个项目进行一些一些头脑风暴扩充;
你随意百度一个类似的项目信息或者是可以运行的项目代码,使用一次。
2、项目的解释必须有一个原则:你的项目的使用环境、预估的访问人数、并发量;
3、项目的开发技术,如果是单节点的开发技术,只需要传统的技术名词:jquery,Struts,hibernate,spring,shiro
4、如果你的项目设计的架构比较复杂,使用的服务节点比较多,这个时候你就需要清楚这些服务节点的作用,以及这些服务节点的安全处理你是如何进行的、节点间的数据互相同步处理;
5、描述这个项目之中具备多少个模块,完成的周期(比喻做一个cms新闻管理系统,两天就可以完成的工作用一个月)项目最快比喻(美工(一周)、测试(2~3天)、开发开发(大约3周,按照一天可以开发3~5(新手2~4)个模块来进行推算,而且一个项目团队的人数不会超过十个人,如果项目是一般的,基本上就可以考虑5、6个人,美工公用的))
6、你做了哪些项目,这些项目里面的具体的业务是什么。
20、项目开发与部署
如果要进行项目的开发一定会有一个源代码程序,但是这个源代码程序如果要想执行,肯定要求将其部署到WEB容器之中,对于WEB容器可以使用Tomcat。
21、项目开发流程
实际上对于项目的开发流程与你的正常做饭吃饭是相同的。
确定你要做的是什么饭,例如:你今天打算吃个火锅?或者吃个炒菜?吃个方便面?
需要为饭菜做一些准备,例如:购买食材、购买调料;
开始进行做饭的手势,例如:洗菜、摘菜、准备各种调料:葱姜蒜;
开始炒制,开始做饭;
开始吃;
开始收尾;
实际上项目的流程和它没有什么区别?
【10%】现在假设你是甲方老板,你需要开发某一款软件,进行某些业务功能的弥补,那么对于此时的乙方相当于获得了你的需求,而后可以针对于你的需求进行分析;将所有可能遇见的功能进行列表以及与甲方进行却,随后确定这个项目可以,这个醒目做的时候出一些项目的执行方案(很多人会拿着你的方案自己去做了,如果你以后真的开公司进行项目开发,那么一定要跟甲方要钱,不给钱说明不给你做,不要给自己的方案给它)。
前期需求的时候一定要给出一些设计界面;
【30%】进行项目的先期规划设计,需要设计出数据库、设计出项目的架构、以及预估项目的访问量,同时需要考虑扩容性问题(你必须对要进行开发的行业很清楚,才能够做出来);
【50%】进行项目的编写开发,而在进行编写开发的过程之中,也会牵扯到一些问题:甲方的需求有可能变更,所以你的项目的结构设计必须满足于变更的需求,那么就需要对项目重构,最好使用Maven进行重构处理;
进行项目的内部测试,而后进行bug记录;
【10%】进行项目的上线以及项目的维护;(新人一般都做维护)
个人需求分析的实现方式:(需求分析没有固定的方法,什么的【10%】代表预付款,最后国内一般要不到)
明确的写出项目的业务流程,例如:该业务涉及到多少张数据表每张表的数据有什么作用?
明确的给出项目规划图(前端开发人员提供);
22、项目设计案例
1、一般的,用户表,课程表,报名数据表的设计是第二范式,需要建立用户数据库,然后通过userid跟课程id来决定报名一个学生的报名情况,然而当前情况是用户账号密码是调用webservice接口获取的,而且用户有可能会被注销,希望老师提供解决思路
2、每一个课程是有名额限制的,假如想让读者看到实时剩余名额,在线抢课的人可能比较多,应如何避免数据不一致的情形发生。
如果要想实现这样的架构设计,并且考虑并发访问的话,那么就需要更多的去解决你的吞吐量问题,现在做一个假设,参与秒杀的人数为5W。
高并发的情况下,不要过多的考虑数据库的设计范式,只需要按照整体的单表处理,所有的操作要通过业务层实现关联处理。
23、延迟加载问题
对于这种延迟加载问题,实际上属于spring+Hibernati时代的产物,这个也是Hibernate比较鸡肋的功能。之所以说它鸡肋主要是因为有如下问题:
1、几乎所有的配置都需要打开延迟加载,否则整个的程序性能会受到严重的影响;
2、有可能会产生严重的“1+N”查询,例如:现在一个分类下有多个新闻信息,那么现在很明显,如果此时没有打开延迟加载,就意味着如果你现在有10个分类的数据,那么就会自动查询10次的字表数据,那么如果每个字表的字段包含有3000篇新闻,那么最终的结果就是30000w条数据,而且这些数据你不是直接去使用。
3、当spring与Hibernate结合的时候就会出现有一个页面数据的延迟打开问题,也就是说你可能希望你的Session一直到页面后才进行关闭。
将Hibernate中的Session延迟到页面后进行关闭,那么对于整个的事务处理是非常麻烦的,而且这种操作还需要进行一个OpenSessionInView过滤器的配置。
正是因为这样的情况,在实际的开发过程之中,针对于需要进行显示的数据,例如:你现在在查询一个雇员的时候,希望取得雇员对应的部门数据,而且很确定这个雇员只对应有一个部门的信息,那么就建议在业务层里面,取得完雇员信息之后,直接再使用getter方法查询该雇员对应的部门的数据信息。
在业务层的控制是因为所有的AOP切入点都是在业务层上完成的。随后到了整个页面之中就可以直接在JSP页面里面利用emp对象“${emp.dept.dname}”。
对于程序的性能,基本上都出现在数据库上,而如果说用户很多,问题也会出现在数据库上,而我们在开发的时候为了保证数据库的操作性能,最好的做法就是:保证你只是查询到你所需要的数据,不需要的数据绝对不要去查,因为查询会浪费磁盘的性能,而查询的结果会占用内存,而CPU是通过内存读取数据的,所以如果数据量一大,或者控制不合理,那么直接导致系统变慢。
24、用户认证流程(Oauth设计)
如果你现在有多个系统需要统一的集成处理,那么就需要有一套统一的空间进行认证数据的保存,但是仅仅是保存认证数据而已。
一般在进行oauth(读:欧奥斯)处理的时候往往会使用spache、oltu组件来完成整体的开发操作。
25、站点访问统计计量架构设计
如果说你现在想做一个全网络的站点访问统计,会包含一些核心的数据:用户从哪里来、访问的地址(UV、PV),访问的次数等,那么对于这样的设计肯定需要一套分布式的处理架构。
现在假设对于所有的中国站点的访问量的设计里面每秒的传输的数据有1000w条,对于1000w条的访问量,那么肯定不能够使用传统的关系型数据库处理了(传统的关系型数据库提供有事物的支持能力,一旦牵扯到了事务问题,那么就直接带来性能降低),所以在这种情况下肯定要使用No-SQL数据库,既然要进行大并发访问,肯定使用就是Redis数据库。而且既然每秒有1000w条