分布式锁的场景与实现
使用场景首先,我们看这样一个场景:客户下单的时候,我们调用库存中心进行减库存,那我们一般的操作都是: update store set num = $num where id = $id 这种通过设置库存的修改方式,我们知道在并发量高的时候会存在数据库的丢失更新,比如 a, b 当前两个事务,查询出来的库存都是 5,a 买了 3 个单子要把库存设置为 2,而 b 买了 1 个单子要把库存设置为 4,那这个时候就会出现 a 会覆盖 b 的更新,所以我们更多的都是会加个条件: update store set num = $num where id = $id and num = $query_num 即乐观锁的方式来处理,当然也可以通过版本号来处理乐观锁,都是一样的,但是这是更新一个表,如果我们牵扯到多个表呢,我们希望和这个单子关联的所有的表同一时间只能被一个线程来处理更新,多个线程按照不同的顺序去更新同一个单子关联的不同数据,出现死锁的概率比较大。对于非敏感的数据,我们也没有必要去都加乐观锁处理,我们的服务都是多机器部署的,要保证多进程多线程同时只能有一个进程的一个线程去处理...
Session 分布式方案
基于 nfs(net filesystem) 的 Session 共享将共享服务器目录 mount 各服务器的本地 session 目录,session 读写受共享服务器 io 限制,不能满足高并发。 基于关系数据库的 Session 共享这种方案普遍使用。使用关系数据库存储 session 数据,对于 mysql 数据库,建议使用 heap 引擎。这种方案性能取决于数据库的性能,在高并发下容易造成表锁(虽然可以采用行锁的存储引擎,性能会下降),并且需要自己实现 session 过期淘汰机制。 基于 Cookie 的 Session 共享这种方案也在大型互联网中普遍使用,将用户的 session 加密序列化后以 cookie 的方式保存在网站根域名下(比如 taobao.com),当用户访问所有二级域名站点式,浏览器会传递所有匹配的根域名的 cookie 信息,这样实现了用户 cookie 化 session 的多服务共享。此方案能够节省大量服务器资源,缺点是存储的信息长度受到 http 协议限制;cookie 的信息还需要做加密解密;请求任何资源时都会将 cookie 附加到 ...
谈谈业务中使用分布式的场景
首先,需要了解系统为什么使用分布式。 随着互联网的发展,传统单工程项目的很多性能瓶颈越发凸显,性能瓶颈可以有几个方面: 应用服务层:随着用户量的增加,并发量增加,单项目难以承受如此大的并发请求导致的性能瓶颈。 底层数据库层:随着业务的发展,数据库压力越来越大,导致的性能瓶颈。 场景1:应用系统集群的 Session 共享应用系统集群最简单的就是服务器集群,比如:Tomcat 集群。应用系统集群的时候,比较凸显的问题是 Session 共享,Session 共享我们一是可以通过服务器插件来解决。另外一种也可以通过 Redis 等中间件实现。 场景2:应用系统的服务化拆分服务化拆分,是目前非常火热的一种方式。现在都在提微服务。通过对传统项目进行服务化拆分,达到服务独立解耦,单服务又可以横向扩容。服务化拆分遇到的经典问题就是分布式事务问题。目前,比较常用的分布式事务解决方案有几种:消息最终一致性、TCC 补偿型事务等。 场景3:底层数据库的压力分摊如果系统的性能压力出现在数据库,那我们就可以读写分离、分库分表等方案进行解决。 三个场景对应”应用层、服务层、数据层”三层瓶颈,框架正确...
Spring 框架中用到了哪些设计模式
代理模式:在 AOP 和 Remoting 中被用的比较多。 单例模式:在 Spring 配置文件中定义的 Bean 默认为单例。 模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。 前端控制器:Spring 提供了 DispatcherServlet 来对请求进行分发。 视图帮助(View Helper ):Spring 提供了一系列的 JSP 标签,高效宏来辅助将分散的代码整合在视图里。 依赖注入:贯穿于 BeanFactory / ApplicationContext 接口的核心理念。 工厂模式:BeanFactory 用来创建对象的实例。 七条清单覆盖了高频答案,但每条都停在”点名”层面。面试与源码阅读真正需要的是:每个模式在 Spring 中的具体落点(类名级)与它解决了什么设计问题。本文把清单升级为”模式 → 落点 → 动机”三元组,并补充清单遗漏的几种。 工厂模式:BeanFactory 与 FactoryBean BeanFactory:简单工厂/工厂方法思...
Spring 的单例实现原理
Spring 对 Bean 实例的创建是采用单例注册表的方式进行实现的,而这个注册表的缓存是 ConcurrentHashMap 对象。 这句话只说对了三分之一:缓存确实是并发 Map,但 Spring 单例的真正复杂度不在”存”,而在”创建过程如何保证只创建一次、且能处理循环依赖“。本文按单例语义、三级缓存、创建流程三层展开。 Spring 单例 ≠ GoF 单例设计模式的单例是”一个类在全 JVM 只有一个实例”(私有构造 + 静态持有);Spring 的单例是”一个 beanName 在容器单例注册表中只有一个实例“(singleton scope)。同一个类可以定义两个 bean(不同 name)各持一个实例,也可以被 prototype scope 反复新建。所以 Spring 单例是容器级的约定,靠注册表约束而非语言机制。 单例注册表:三级缓存DefaultSingletonBeanRegistry 中维护三个 Map: singletonObjects(一级):完成品缓存,存放完全初始化好的 bean earlySingletonObjects(二级):半成品缓存...
Spring MVC 启动流程
在 web.xml 文件中给 Spring MVC 的 Servlet 配置了 load-on-startup,所以程序启动的时候会初始化 Spring MVC,在 HttpServletBean 中将配置的 contextConfigLocation 属性设置到 Servlet 中,然后在 FrameworkServlet 中创建了 WebApplicationContext,DispatcherServlet 根据 contextConfigLocation 配置的 classpath 下的 xml 文件初始化了 Spring MVC 总的组件。 这段描述对应传统 war 包 + web.xml 时代。今天主流是 Spring Boot 内嵌容器 + 自动配置,启动链路已完全不同;但两条链路的终点相同:DispatcherServlet 初始化时完成九大组件的装配。本文双链路对照讲清,并落到”组件初始化”这一共同核心。 传统链路:web.xml + 三层继承 容器(Tomcat)启动,读 web.xml,按 load-on-startup 顺序实例化 Servlet Http...
Spring MVC 运行流程
Spring MVC 将所有的请求都提交给 DispatcherServlet,它会委托应用系统的其他模块负责对请求进行真正的处理工作。 DispatcherServlet 查询一个或多个 HandlerMapping,找到处理请求的 Controller. DispatcherServlet 请求提交到目标 Controller Controller 进行业务逻辑处理后,会返回一个 ModelAndView Dispatcher 查询一个或多个 ViewResolver 视图解析器,找到 ModelAndView 对象指定的视图对象 视图对象负责渲染返回给客户端。 六步主线是经典 MVC 视图渲染路径,但现代 REST 接口早已不走 ViewResolver(直接写 JSON),且主线之外还有四个关键环节常被遗漏:过滤器/拦截器链、参数解析与返回值处理、异常解析、异步处理。本文补成完整请求生命周期。 完整请求生命周期 容器接收:Servlet 容器(Tomcat)把请求交给 DispatcherServlet(前端控制器,url-pattern ...
Spring 事务底层原理
划分处理单元 IOC由于 Spring 解决的问题是对单个数据库进行局部事务处理的,具体的实现首相用 Spring 中的 IOC 划分了事务处理单元。并且将对事务的各种配置放到了 IOC 容器中(设置事务管理器,设置事务的传播特性及隔离机制)。 AOP 拦截需要进行事务处理的类Spring 事务处理模块是通过 AOP 功能来实现声明式事务处理的,具体操作(比如事务实行的配置和读取,事务对象的抽象),用 TransactionProxyFactoryBean 接口来使用 AOP 功能,生成 proxy 代理对象,通过 TransactionInterceptor 完成对代理方法的拦截,将事务处理的功能编织到拦截的方法中。读取 IOC 容器事务配置属性,转化为 Spring 事务处理需要的内部数据结构(TransactionAttributeSourceAdvisor),转化为 TransactionAttribute 表示的数据对象。 对事物处理实现(事务的生成、提交、回滚、挂起)Spring 委托给具体的事务处理器实现。实现了一个抽象和适配。适配的具体事务处理器:DataSour...
如何自定义注解实现功能
1.创建自定义注解和创建一个接口相似,但是注解的 interface 关键字需要以 @ 符号开头。 2.注解方法不能带有参数; 3.注解方法返回值类型限定为:基本类型、String、Enums、Annotation 或者是这些类型的数组; 4.注解方法可以有默认值; 5.注解本身能够包含元注解,元注解被用来注解其它注解。 五条语法规则是定义注解的边界,但”实现功能”的关键不在注解本身——注解只是元数据,功能来自处理它的处理器。在 Spring 生态里处理器有四类形态:AOP 切面、BeanPostProcessor、ImportBeanDefinitionRegistrar、以及启动时扫描器。本文按”定义 → 选处理器 → 完整示例”给出可复制的套路。 定义回顾与 Spring 场景元注解@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface RateLimit { int permits() default 10; //...
Spring 事务实现方式
编码方式所谓编程式事务指的是通过编码方式实现事务,即类似于 JDBC 编程实现事务管理。 声明式事务管理方式声明式事务管理又有两种实现方式: 基于 xml 配置文件的方式; 另一个实在业务方法上进行 @Transaction 注解,将事务规则应用到业务逻辑中; 三种方式的清单只回答了”有几种”,工程上真正需要的是:每种怎么写、何时选哪种、以及声明式事务最常踩的失效场景。本文补齐成可操作手册。 编程式事务:TransactionTemplate@Autowired TransactionTemplate txTemplate;public void transfer(long from, long to, BigDecimal amt) { txTemplate.executeWithoutResult(status -> { dao.debit(from, amt); if (riskCheck(from)) { status.setRollbackOnly(); return; } // 显式...















