疾风式全栈(14)-生产环境准备(草稿)

至此, 我们已经接触了全栈开发中实现基本功能的各种技术, 如果只是学习和测试, 实现一个在页面上展示和操作服务器端数据的系统是没有什么问题了. 可是如果我们想要实现一个在实际生产环境中可用的系统, 还有几个重要的方面要处理.

授权访问控制

一般的软件系统都会有用户模块. 数据库和操作系统也都有用户验证机制. 而自己开发的系统, 一般是在数据库中存储用户信息, 可能还包括角色和相应权限等. 客户端对于需要授权访问的功能, 首先会要求登录, 发送用户验证信息到服务器, 和数据库中的数据比对一致后, 返回成功信息给客户端, 才允许访问. 而目前的多数web系统会采用session和cookie的机制实现对用户登录状态的管理. 即在用户登录成功后, 在服务器内存中保存客户端的登录状态. 这种数据称为session. session数据有一定的时效性, 比如在tomcat中默认是30分钟. 时效范围之内, 来自同一客户端的访问, 都会被认为属于同一session, 具体方式是将session id也就是每个session的唯一编号储存在客户端cookie中, 浏览器每次发起访问请求都会携带对应域名下的cookie信息.

cookie和session的机制简单而有效, 不过现在也有了其他的形式. 主要是非web化的客户端和无状态的服务端.
如果客户端不是浏览器, 那么依赖cookie的web架构就需要做一些改变. 不过总的来说, 思路是一致的, 就是在客户端存储中保存一个客户端的唯一标志, 请求时携带这个标志用以区分客户端. 其实现在通过新一代的web标准, 浏览器也有了更多的客户端存储技术, localstorage和sessionstorage已经非常成熟和通用了, 使用也很方便, 足以应对大多数场景. IndexedDB也不错, web sql虽然出现较早, 却没有成为通用的标准. 不过也可以采用js库比如AlaSQL, 在前端处理数据存储.

服务器端主要是通过密钥发放机制, 在每次访问时验证密钥, 避免存储session信息导致的扩展和同步问题. 其实HTTP协议是无状态的, session的作用就是在一段时间内保存连接的状态. 不过随着时间人们发现在内存中存储的session限制了服务器的扩展和维护. 于是人们想办法把session改造成可以持久化和分布式的. 或者采用密钥机制, 不用session存储状态. 这样理论上服务器端可以随意扩展, 加减机器和启停撤换都不会影响对外提供服务.

从这里我们可以看到, cookie也好, session也好, 这些问题的核心都是存储. 客户端存储, 内存存储, 分布式存储, 各种技术的火热, 表明计算的核心问题是存储. 在软件技术里, 数据结构也是最核心的学科. 希望大家重视.

对于访问控制的具体实现, 在java里有spring security或者shiro这样的框架, 不过在简单的情况下, 自己在过滤器里指定一下控制规则就可以. 而像node中的koa2这样的技术, 更是完全基于过滤器的, 在koa2里叫中间件, 不过思想是一样的.

日志

另一个重要的问题是日志. 软件上线运行之后, 我们需要软件时刻都正常稳定的运行, 但是又不能随时有人看着, 那么一个重要的机制就是做好日志记录. 对于软件运行过程中的关键节点的信息, 以写入日志文件的方式保存下来. 可能有些信息是技术方面的, 有些是用于业务流程追溯和分析的. 对于生产系统中的软件, 日志是重要的部分, 各种开发技术和框架都提供了日志功能的支持. 比如log4j和log4js. 服务器软件如tomcat, nginx也都有自己的日志. 包括操作系统和数据库也不例外. 配置各种日志, 以便在需要时通过查看日志获取需要的信息, 是技术工作的重要部分. 日志应该记录重要和必要的信息, 过多的日志操作也会对服务器性能造成影响. 现在有了各种异步日志技术, 不过还是要根据整体情况综合选择.

性能

说起性能, 话题就更多了. 我们之前提过, 提高IO性能的关键在于异步操作, 也就是说, 对于短时间无法处理的请求, 通过排队的方式先保存起来, 再择机处理. 本质上是以空间换时间, 存储换计算. nginx和node都是基于异步模型的, 性能高于传统的同步阻塞模型. java也引入了NIO和AIO技术, 不过据说实际性能不如社区开发的Netty. Netty使用起来也比官方的API方便, 已经称为了java世界异步通讯技术的基础.
异步模型也并非万能, 只是对于IO密集型的场景, 扩展了传统同步模型的能力. 但是如果发生长时间的计算, 需要占用大量CPU时间, 就要提前做好准备, 分配到其他线程或其他CPU和机器上.

对于性能其实话题太多, 很多都是在生产环境中, 并发量达到一定数量之后才有的问题. 所以关键是如何让利用好有限的时间和资源. 至少要提前了解, 系统大概能承受到什么级别的并发量, 超过之后系统会如何处理, 有没有可能造成难以挽回的业务损失. 一般在上线前要通过模拟并发对系统性能进行压力测试, 确定各种并发量下系统的反应能满足需求, 即使服务中断, 也能够迅速恢复, 并且业务数据可以回溯, 不要丢失关键的业务数据.

稳定性

这里其实也就涉及到了稳定性测试的问题. 大并发导致的性能压力是影响稳定性的一个方面. 另一方面就是各种意外情况. 测试的时候我们可能会对能想到的常见意外进行模拟, 比如停电, 断网, 不合法的数据等. 我们无法阻止意外的发生, 但是我们的目标是, 意外尽可能不影响系统运行, 至少是要可恢复, 并且关键业务数据不能丢失.

异常

所以在各种现代语言里都有异常处理的机制. 我们在之前只是关注实现功能, 想的都是正常的情况. 但是当正常情况下功能都可以实现了, 就要考虑非正常情况了. 优秀的软件和一般的软件的区别就在于考虑了各种意外情况, 而这些很多都来源于长期的测试和大量的经验积累.
java和js提供的异常处理机制形式差不多, 都是try和catch. 把有可能出现异常的代码放在try块中, 发生的异常用catch捕获, 然后指定处理方式. 但是在java中有受检异常, 也就是必须用try和catch处理的异常, 否则视为语法错误, 不允许编译. 这是java的特色, 其他语言一般都是自己选择哪些需要处理. 对于没有处理的异常按照系统默认的处理方式, 一般是停止运行, 输出错误信息.

安全

另外一个宽泛和重要的概念就是安全. 其实我们上面讨论的这些方面也都可以归为安全问题. 但是安全涉及的方面可能更多. 对此我们不想过多讨论. 只是随着越来越多接触软件这个行业, 我更多认识到, 好软件是测试出来的, 而不是开发出来的. 我们上面所说的所有这些问题, 可能在开发的时候我们都没想到, 我们想到的问题可能也没有发生, 所以测试很重要, 一切以实际测试结果为准. 测试驱动开发, 能够避免我们很多空想和漫无目标的开发. 当然, 测试是一门大学问, 具体如何执行, 还是要根据实际业务场景来定.

所以, 生产系统中的软件是要考虑很多方面的. 如果我们只关注一般情况下功能可以使用, 软件可能是非常简单的. 但是如果我们要开发出在生产环境下运行的软件, 那么就会有大量的日志, 异常, 甚至优化性能的代码需要加进来. 最终的代码量可能翻了好几倍, 再也不是我们开始写的那样简单明了. 怎样让软件的基本流程和基本功能, 与生产环境中要处理的额外细节更好的隔离开, 让软件始终保持清晰简单的结构, 是一个重要的研究课题.

你可能感兴趣的:(疾风式全栈(14)-生产环境准备(草稿))