request
:封装客户端的请求,其中包含来自GET或POST请求的参数;
response
:封装服务器对客户端的响应;
pageContext
:通过该对象可以获取其他对象;
session
:封装用户会话的对象;
application
:封装服务器运行环境的对象;
out
:输出服务器响应的输出流对象;
config
:Web应用的配置对象;
page
:JSP页面本身(相当于Java程序中的this);
exception
:封装页面抛出异常的对象。
page
代表与一个页面相关的对象和属性。
request
代表与Web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个Web组件;需要在页面显示的临时数据可以置于此作用域。
session
代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的session中。
application
代表与整个Web应用程序相关的对象和属性,它实质上是跨越整个Web应用程序,包括多个页面、请求和会话的一个全局作用域。
Redirect
:重定向,服务器告诉浏览器请求的URI,由浏览器再次向新的地址发起请求
Forward
:转发,由服务器内部执行跳转,共享request数据,并将执行结果告诉浏览器,浏览器并没有感知到地址发生变化。
simpleDateFormat不是线程安全的。参考下面线程安全的方法:
// 1. 在方法内部使用,没有线程安全问题 private static final String FORMAT = "yyyy-MM-dd HH:mm:ss"; public String getFormat(Date date){ SimpleDateFormat dateFormat = new SimpleDateFormat(FORMAT); return dateFormat.format(date); } // 2. 每次使用的时候加锁 private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public void getFormat(){ synchronized (SIMPLE_DATE_FORMAT){ SIMPLE_DATE_FORMAT.format(new Date()); ….; } // 3. 使用ThreadLocal,每个线程都有自己的SimpleDateFormat对象,互不干扰 private static final ThreadLocal<DateFormat> DATE_FORMATTER = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); } }; // 4. 使用DateTimeFormatter(This class is immutable and thread-safe.) DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); System.out.println(timeFormatter.format(LocalDateTime.now()));
工厂模式:Spring通过工厂模式创建bean
代理模式:Spring AOP通过动态代理增强原对象的功能
责任链模式:SpringMVC的过滤器
单例模式:spring构建的对象默认是单例
public class SingletonClass { private static final SingletonClass instance = new SingletonClass(); public static SingletonClass getInstance() { return instance; } private SingletonClass() { } }
XSS又叫CSS (Cross Site Script) ,跨站脚本攻击。它指是恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入Web里面的html代码会被执行,从而达到恶意用户的特殊目的。
-client -server模式
-Xmn、-Xms、-Xmx
监控:jps、jstat、jinfo、jmap、jhat、jstack…
自动化程度上:hibernate是全自动化的orm框架,提供了对象到数据库的完全映射和sql的内部自动生成,其对象映射是指pojo到整张数据表的映射。而ibatis则是半自动化的,其对象映射是指ibatis提供了sql语句的参数到pojo的映射,sql语句的返回到pojo的映射,而且sql语句并不自动生成,由开发者手动编写和维护。
框架灵活性:自动化程度上的区别,直接影响了框架灵活性的区别。比如,在场景A中,要查询表的部分字段,在场景B中,要查询表的所有字段,hibernate由于只有一个配置文件,要么就部分字段lazy load,要么就全部查询出来,而且默认是(全部查询的),就无法适用2个场景,灵活性差。但要是ibatis,则可以通过不同场景使用不同sql语句来满足,并且不同的场景下,也可以有自己的sql语句的参数pojo映射和返回结果pojo映射,互不影响,这就灵活的适应了不同场景的要求。
性能上的区别:在海量数据和数据库性能要求苛刻的场合,往往是通过高度优化的sql语句来实现的,而这种情况下,hibernate则无法实现,但ibatis则可能通过sql的优化,来进一步提升数据库代码的性能。
学习门槛和成本:ibatis比hibernate更容易上手,而且hibernate也更复杂一些。
left join
(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录
right join
(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录
inner join
(等值连接) 只返回两个表中联结字段相等的行
索引的目的在于提高查询效率,索引就像是书的目录,是与表或视图关联的磁盘上结构,可以加快从表或视图中检索行的速度。
索引的原理在于使用空间换时间,索引会将建立的索引KEY存储在N叉树(BTree)。BTree适合在磁盘存储设备上组织动态查找表,这些数据结构以某种方式引用(指向)数据。
唯一索引:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
主键索引:是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。一般是在建表的时候同时创建主键索引。
组合索引:指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合。
普通索引:最基本的索引,它没有任何限制,用于加速查询。
全文索引:主要用来查找文本中的关键字,而不是直接与索引中的值相比较。
如果把业务切割得足够独立,那把不同业务的数据放到不同的数据库服务器将是一个不错的方案,而且万一其中一个业务崩溃了也不会影响其他业务的正常进行,并且也起到了负载分流的作用,大大提升了数据库的吞吐能力。
按一定规则(按哈希/按时间/按区间)分组,并把该组的数据存储到一个数据库分片中,这样随着数据量的增加,只要简单地配置一台服务器即可。
如何来确定某个用户所在的shard呢,可以建一张用户和shard对应的数据表,每次请求先从这张表找用户的shard id,再从对应shard中查询相关数据。
原子性(Atomicity):操作这些指令时,要么全部执行成功,要么全部不执行。
一致性(Consistency):事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。
隔离性(Isolation):隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
持久性(Durability):当事务正确完成后,它对于数据的改变是永久性的。
Read uncommitted:最低级别,任何情况都无法保证
Read committed:可避免脏读的发生
Repeatable read:可避免脏读、不可重复读的发生
Serializable:可避免脏读、不可重复读、幻读的发生
乐观锁不是数据库自带的,需要我们自己去实现。乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。
悲观锁就是在操作数据时,认为此操作会出现数据冲突,所以在进行每次操作时都要通过获取锁才能进行对相同数据的操作,这点跟java中的synchronized很相似,所以悲观锁需要耗费较多的时间。另外与乐观锁相对应的,悲观锁是由数据库自己实现了的,要用的时候,我们直接调用数据库的相关语句就可以了。
共享锁指的就是对于多个不同的事务,对同一个资源共享同一个锁。相当于对于同一把门,它拥有多个钥匙一样。就像这样,你家有一个大门,大门的钥匙有好几把,你有一把,你女朋友有一把,你们都可能通过这把钥匙进入你们家,进去啪啪啪啥的,一下理解了哈,没错,这个就是所谓的共享锁。
排它锁与共享锁相对应,就是指对于多个不同的事务,对同一个资源只能有一把锁。与共享锁类型,在需要执行的语句后面加上for update就可以了。
行锁由字面意思理解,就是给某一行加上锁,也就是一条记录加上锁。
表锁和行锁相对应,给这个表加上锁。
两阶段提交(2PC/3PC)
补偿事务(TCC)
MQ 事务消息,最终一致性方案
BeanFactory
是Spring中最基本、通用的工厂,这里的工厂,其不仅仅是构造实例,它可以创建并管理各种类的对象,即是Spring IOC容器体系结构的基本接口,其与其子接口便构成了Spring IOC容器的体系结构(如下图:IOC容器主要接口关系图)。所以不要把它理解成常规意义的简单工厂,也可以把它称为Sring容器。
ApplicationContext
也是一个Spring容器,它是由BeanFactory接口派生而来,因而提供BeanFactory所有的功能,此外它还扩展了BeanFactory的功能,提供了更多的高级功能。
所以联系上它们本身都是Spring提供的接口,它们都和其子接口构成了Spring IOC的容器,我们一般都称他们为Spring容器。其中BeanFactory是Spring框架的基础设施,面向Spring本身,其提供基本的功能,如:getBean(),;ApplicationContext是建立在它之上,面向使用者,扩展了更多面向应用的功能,更易于创建实际应用,实际也基本使用它。
区别上:由上述可知他们提供的功能是有不同的,体系结构、用途等有所不同。另外他们在初始化时有一个重大的区别:BeanFactroy初始化容器时,并未初始化Bean,直到第一次在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化(延迟加载),这样的话,我们就不能在初始化容器时发现一些存在的Spring的配置问题。而ApplicationContext则相反,它是在容器启动时,就一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。
在 Spring 中管理事务会涉及到这几个属性,事务隔离级别、是否只读、事务的传播行为,说到事务的传播行为,指的就是不同的业务方法之间相互调用时,应该如何管理事务。
Spring的事务管理机制实现的原理,就是通过这样一个动态代理对所有需要事务管理的Bean进行加载,并根据配置在invoke方法中对当前调用的 方法名进行判定,并在method.invoke方法前后为其加上合适的事务管理代码,这样就实现了Spring式的事务管理。
spring的AOP底层是由 JDK提供的动态代理技术 和 CGLIB(动态字节码增强技术)实现。
IOC
(Inversion of Control,控制反转)是spring的核心,贯穿始终。所谓IOC,对spring框架来说,就是由spring来控制对象的生命周期和对象间的依赖关系。
传统模式:对象间的依赖关系由对象自己控制,代码耦合度高。
IOC开发模式:对象间的依赖关系由spring容器来控制,解耦。
AOP
面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
主要功能是:日志记录、性能统计、安全控制、事务处理、异常处理等等。
spring springMvc
springMvc springBoot
springboot springcloud
Container
负责启动加载运行服务提供者Provider。根据Provider配置的文件根据协议发布服务 , 完成服务的初始化。 Provider
在启动时,根据配置中的Registry地址连接Registry,将Provider的服务信息发布到Registry,在Registry注册自己提供的服务。 Consumer
在启动时,根据消费者XML配置文件中的服务引用信息,连接到Registry,向Registry订阅自己所需的服务。 Registry
根据服务订阅关系,返回Provider地址列表给Consumer,如果有变更,Registry会推送最新的服务地址信息给Consumer。 Consumer
调用远程服务时,会根据路由策略,先从缓存的Provider地址列表中选择一台进行,跨进程调用服务,假如调用失败,再重新选另一台调用。 Provider
和 Consumer
,会在内存中记录调用次数和调用时间,每分钟发送一次统计数据到Monitor。 当服务消费方调用服务提供者失败后自动切换到其他服务提供者服务器进行重试。这通常用于读操作或者具有幂等的写操作,需要注意的是重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。
当服务消费方调用服务提供者失败后,立即报错,也就是只调用一次。通常这种模式用于非幂等性的写操作。
当服务消费者调用服务出现异常时,直接忽略异常。这种模式通常用于写入审计日志等操作。
当服务消费端用服务出现异常后,在后台记录失败的请求,并按照一定的策略后期再进行重试。这种模式通常用于消息通知操作。
当消费方调用一个接口方法后,Dubbo Client会并行调用多个服务提供者的服务,只要一个成功即返回。这种模式通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。
当消费者调用一个接口方法后,Dubbo Client会逐个调用所有服务提供者,任意一台调用异常则这次调用就标志失败。这种模式通常用于通知所有提供者更新缓存或日志等本地资源信息。
按照概率设置权重,比较均匀,并且可以动态调节提供者的权重。
轮询,按公约后的权重设置轮询比率。会存在执行比较慢的服务提供者堆积请求的情况,比如一个机器执行的非常慢,但是机器没有挂调用(如果挂了,那么当前机器会从Zookeeper的服务列表删除),当很多新的请求到达该机器后,由于之前的请求还没有处理完毕,会导致新的请求被堆积,久而久之,所有消费者调用这台机器上的请求都被阻塞。
如果每个提供者的活跃数相同,则随机选择一个。在每个服务提供者里面维护者一个活跃数计数器,用来记录当前同时处理请求的个数,也就是并发处理任务的个数。所以如果这个值越小说明当前服务提供者处理的速度很快或者当前机器的负载比较低,所以路由选择时候就选择该活跃度最小的机器。如果一个服务提供者处理速度很慢,由于堆积,那么同时处理的请求就比较多,也就是活跃调用数目越大,这也使得慢的提供者收到更少请求,因为越慢的提供者的活跃度越来越大。
一致性Hash,可以保证相同参数的请求总是发到同一提供者,当某一台提供者挂了时,原本发往该提供者的请求,基于虚拟节点,平摊到其他提供者,不会引起剧烈变动。
国内采用的MQ有: ActiveMQ
、 Kafka
、 RabbitMQ
、 RocketMQ
,但现在 ActiveMQ
用的越来越少了。主要做其他三种的调研。
kafka | RabbitMQ | RocketMQ | |
---|---|---|---|
开发语言 | Scala开发 | Erlang开发 | java开发 |
性能 | 吞吐量:吞吐量所有MQ里最优秀,QPS十万级、性能毫秒级、支持集群部署 | 吞吐量比较低,QPS几万级、性能u秒级、主从架构 | 吞吐量高,QPS十万级、性能毫秒级、支持集群部署 |
功能 | 功能单一 | 功能单一 | 支持各种高级功能,比如说延迟消息、事务消息、消息回溯、死信队列、消息积压等等 |
缺点 | 丢数据, 因为数据先写入磁盘缓冲区,未直接落盘。机器故障会造成数据丢失 | Erlang小众语言开发,吞吐量低,集群扩展麻烦 | 官方文档相对简单可能是RocketMQ目前唯一的缺点 |
应用场景 | 适当丢失数据没有关系、吞吐量要求高、不需要太多的高级功能的场景,比如大数据场景 | 中小公司对并发和吞吐量要求不高的场景 | 适当丢失数据没有关系、吞吐量要求高、不需要太多的高级功能的场景,比如大数据场景 |
zookeeper默认是采用投票数大于半数则胜出的逻辑。
目前有5台服务器,每台服务器均没有数据,它们的编号分别是1,2,3,4,5,按编号依次启动,它们的选择举过程如下:
当redis集群的主节点故障时,Sentinel集群将从剩余的从节点中选举一个新的主节点。
这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。
使用一致性哈希对数据进行映射
实现方式:客户端分片(每个客户端对应一个分片)、代理协助分片、查询路由分片;
使用redis集群(依赖zookeeper);
红黑树放弃了追求完全平衡,追求大致平衡,在与平衡二叉树的时间复杂度相差不大的情况下,保证每次插入最多只需要三次旋转就能达到平衡,实现起来也更为简单。
平衡二叉树追求绝对平衡,条件比较苛刻,实现起来比较麻烦,每次插入新节点之后需要旋转的次数不能预知。
public static void main(String[] args) { int size = 10000000; System.out.println("执行a方案:"); a(size); System.out.println("执行b方案:"); b(size); } public static void a(int size){ long time = System.currentTimeMillis(); Set<Integer> result = new HashSet<>(size); Random random = new Random(); while (result.size() < size) { int i = random.nextInt(size); result.add(i); } System.out.println("执行耗时 : " + (System.currentTimeMillis() - time) / 1000f + " 秒 "); } public static void b(int size){ long time = System.currentTimeMillis(); int[] list = new int[size]; for(int i = 0; i < size; i++){ list[i] = i; } Random random = new Random(); int[] result = new int[size]; for(int i = 0; i < size; i++){ int index = random.nextInt(size - i); result[i] = list[index]; list[index] = list[size - i - 1]; } System.out.println("执行耗时 : " + (System.currentTimeMillis() - time) / 1000f + " 秒 "); }
public static void main(String[] args) { int[] a = {2,5,6}; int[] b = {3,7,9}; int[] c = mergeSorted(a, b); System.out.println("排序后:" + Arrays.toString(c)); } public static int[] mergeSorted(int[] a, int[] b){ int an = 0; int bn = 0; int[] c = new int[a.length + b.length]; for (int i = 0; i < c.length; i++) { if (an < a.length && bn < b.length) { if (a[an] > b[bn]) { c[i] = b[bn]; bn++; } else { c[i] = a[an]; an++; } } else if (an < a.length) { c[i] = a[an]; an++; } else if (bn < b.length) { c[i] = b[bn]; bn++; } } return c; }
cd
命令用来改变所在目录
ls
命令用来查看目录的内容
cp
命令用来拷贝文件cp
mv
命令把文件移动到另一个目录
mkdir
命令创建目录
tar
命令用于压缩解压
vi/vim
是使用vi编辑器的命令
cat
这个命令常用来显示文件内容
wget
是从远程下载的工具
ifconfig
命令用来查看和配置网络设备
ps
命令显示运行的进程
kill
命令用于终止进程
free
命令用于显示Linux系统内存
top
命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况