文章目录
- 1. 分布式
- 2. 微服务
- SpringCloud原理
- eureka原理
- zookeeper为何能作为注册中心?说下大概实现思路?还有什么常见使用场景?
- Hystrix原理
- Hystrix的内部处理逻辑
- Zookeeper和Eureka区别
- Dubbo服务特点?画出Duboo架构?说明调用关系
- dubbo 提供了哪三个关键功能
- Dubbo中zookeeper做注册中心,如果注册中心集群都挂掉,发布者和订阅者之间还能通信么?
- dubbo 配置 优先级
- dubbo配置覆盖策略
- Dubbo配置来源有几种?分别是?
- 如何禁用某个服务的启动检查?
- 开发测试环境如何绕过注册中心?
- dubbo 使用什么通信框架?
- dubbo默认的序列化框架?你还知道哪些?
- 一个服务有多重实现时,如何处理?
- Dubbo服务调用默认是阻塞的?还有其他的?
- Dubbo服务追踪解决方案?
- 4. 设计模式
- 6. WEB
1. 分布式
分布式锁应该具备哪些条件
- 在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行
- 高可用的获取锁与释放锁
- 高性能的获取锁与释放锁
- 具备可重入特性(可理解为重新进入,由多于一个任务并发使用,而不必担心数据错误)
- 具备锁失效机制,防止死锁
- 具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败
- 加锁解锁过程必须具有原子性
数据库分布式锁
- 创建一个锁表,使用将方法名设置为唯一索引
- 抢占锁时,进行insert数据,成功则获取到锁
- 释放锁时delete数据
有几点问题:
- 数据库挂掉怎么办?
使用多个备份数据库,加锁前进行数据同步,一个挂掉迅速切换到备用库 - 抢占到锁后,未来得及释放锁宕机?
表中添加一个时间戳字段,使用定时任务删除过期的锁。 - 插入失败就会抛异常,如何保证阻塞?
使用while死循环,直到插入成功 - 如何保证重入?
在数据库表中加个字段,记录当前获得锁的机器的主机信息和线程信息,那么下次再获取锁的时候先查询数据库,如果当前机器的主机信息和线程信息在数据库可以查到的话,直接把锁分配给他就可以了。
redis分布式锁
1. 使用setnx命令
- 加锁:setnx(lock_key,val),根据返回结果若值设置成功,则key不存在,加锁成功,反之key已经存在,加锁失败。
- 解锁:del(lock_key)
- 死锁问题:线程1获取锁成功,在未执行完任务时挂掉,没有显示的释放锁,那么其它线程就永远无法获取改锁造成死锁。所以需要设置过期时间,可以利用
expire命令,但是setnx和expire命令是两个动作无法保证加锁操作原子性。还有个问题,假设线程1设置锁成功,但是任务没有执行完时锁已经超时,此时线程2抢占了锁,然后线程1执行完了进行del解锁,此时将会错误的对线程2进行锁释放。
2. 使用set(locl_key,val ,expire_time,NX)命令
针对setnx的问题,可以利用set(locl_key,val ,expire_time,NX)命令,该命令类似setnx并且可以设置过期时间,同时让获得锁的线程开启一个守护线程,使用expire命令用来给快要过期的锁“续航”。比如,设置过期时间为60s,每当50s该key还存在时就进行续命50s。
Zookeeper分布式锁
Zookeeper是树形数据结构,类似于文件目录,树是由节点(Znode)组成,每个Znode都拥有唯一路径,数据也会存储在Znode下。Zookeeper的节点有以下四种类型:
- 持久节点(PERSISTENT):默认的节点类型。创建节点的客户端与 Zookeeper 断开连接后,该节点依旧存在。
- 持久节点顺序节点(PERSISTENT_SEQUENTIAL):所谓顺序节点,就是在创建节点时,Zookeeper 根据创建的时间顺序给该节点名称进行编号:
- 临时节点(EPHEMERAL):和持久节点相反,当创建节点的客户端与 Zookeeper 断开连接后,临时节点会被删除
- 临时顺序节点(EPHEMERAL_SEQUENTIAL):有顺序的临时节点
1. 获取锁
- 创建一个持久节点ParentLock
- 当第一个客户端想要获得锁时,需要在 ParentLock 这个节点下面创建一个临时顺序节点 Lock1。
- Client1 查找 ParentLock 下面所有的临时顺序节点并排序,判断自己所创建的节点 Lock1 是不是顺序最靠前的一个。如果是第一个节点,则成功获得锁。
- 这时候,如果再有一个客户端 Client2 前来获取锁,则在 ParentLock 下载再创建一个临时顺序节点 Lock2。
- Client2 查找 ParentLock 下面所有的临时顺序节点并排序,判断自己所创建的节点 Lock2 是不是顺序最靠前的一个,结果发现节点 Lock2 并不是最小的。
- Client2 向排序仅比它靠前的节点 Lock1 注册 Watcher,用于监听 Lock1 节点是否存在。这意味着 Client2 抢锁失败,进入了等待状态。
- 同样的,假如Client3也来抢占锁,就会监听Lock2,这样一来,Client1 得到了锁,Client2 监听了 Lock1,Client3 监听了 Lock2。这恰恰形成了一个等待队列。
2. 释放锁
释放锁分为两种情况:
- 任务完成,客户端显示释放:当任务完成时,Client1 会显示调用删除节点 Lock1 的指令。
- 任务执行过程中,客户端崩溃:获得锁的 Client1 在任务执行过程中,如果崩溃,则会断开与 Zookeeper 服务端的链接。根据临时节点的特性,相关联的节点 Lock1 会随之自动删除。
由于 Client2 一直监听着 Lock1 的存在状态,当 Lock1 节点被删除,Client2 会立刻收到通知。这时候 Client2 会再次查询 ParentLock 下面的所有节点,确认自己创建的节点 Lock2 是不是目前最小的节点。如果是最小,则 Client2 顺理成章获得了锁。
分布式事务:2PC(two phase commit)两阶段提交
1. 概念
2PC即两阶段提交协议,是将整个事务流程分为两个阶段,准备阶段(Prepare phase ) 、 提交阶段(commit phase ),2是指两个阶段,P是指准备阶段C是指提交阶段
- 准备阶段(Prepare phase) :事务管理器给每个参与者发送Prepare消息, 每个数据库参与者在本地执行事务, 并写本地的Undo/Redo日志, 此时事务没有提交。(Undo日志是记录修改前的数据, 用于数据库回滚, Redo日志是记录修改后的数据, 用于提交事务后写入数据文件)
- 提交阶段(commit phase) :如果事务管理器收到了参与者的执行失败或者超时消息时,直接给每个参与者发送回滚(Rollback) 消息; 否则, 发送提交(Commit) 消息; 参与者根据事务管理器的指令执行提交或者回滚操作,并释放事务处理过程中使用的锁资源。注意:必须在最后阶段释放锁资源。
2. XA解决方案
2PC的传统方案是在数据库层面实现的, 如Oracle、MySQL都支持2PC协议, 为了统一标准减少行业内不必要的对接成本, 需要制定标准化的处理模型及接口标准, 国际开放标准组织Open Group定义了分布式事务处理模型DTP(Distributed Transaction Processing Reference Model) 。为了让大家更明确XA方案的内容程,下面新用户注册送积分为例来说明:
- 执行流程如下:
- 应用程序(AP)持有用户库和积分库两个数据源。
- 应用程序(AP)通过TM通知用户库RM新增用户,同时通知积分库RM为该用户新增积分,RM此时并未提交事务,此时用户和积分资源锁定。
- TM收到执行回复,只要有一方失败则分别向其他RM发起回滚事务,回滚完毕,资源锁释放。
- TM收到执行回复,全部成功,此时向所有RM发起提交事务,提交完毕,资源锁释放。
- DTP模型定义如下角色:
- AP(Application Program) :即应用程序, 可以理解为使用DTP分布式事务的程序。
- RM(ResourceManager) :即资源管理器, 可以理解为事务的参与者, 一般情况下是指一个数据库实例, 通过资源管理器对该数据库进行控制,资源管理器控制着分支事务。
- TM(TransactionManager) :事务管理器, 负责协调和管理事务, 事务管理器控制着全局事务, 管理事务生命周期,并协调各个RM。全局事务是指分布式事务处理环境中,需要操作多个数据库共同完成一个工作,这个工作即是一个全局事务。
- 以上三个角色之间的交互方式如下:
- TM向AP提供应用程序编程接口,AP通过TM提交及回滚事务。
- TM交易中间件通过XA接口来通知RM数据库事务的开始、结束以及提交、回滚等。
- XA的由来:DTP模型定义TM和RM之间通讯的接口规范叫X A, 简单理解为数据库提供的2PC接口协议, 基于数据库的X A协议来实现2PC又称为XA方案。
- 总结
整个2PC的事务流程涉及到三个角色AP、RM、TM。AP指的是使用2PC分布式事务的应用程序;RM指的是资源管理器,它控制着分支事务;TM指的是事务管理器,它控制着整个全局事务。
a. 在准备阶段RM执行实际的业务操作,但不提交事务,资源锁定;
b. 在提交阶段TM会接受RM在准备阶段的执行回复,只要有任一个RM执行失败,TM会通知所有RM执行回滚操作,否则,TM将会通知所有RM提交该事务。提交阶段结束资源锁释放。 - XA方案的问题:
- 需要本地数据库支持XA协议。
- 资源锁需要等到两个阶段结束才释放,性能较差。
2. 微服务
SpringCloud原理
SpringCloud的核心组件有:Eureka、Ribbon、Feign、Hystrix、Zuul。
-
微服务将模块服务化,他们之间会相互调用,随着业务增多,服务增多服务间管理逐渐复杂,Eureka Server提供服务注册功能,服务启动后会将自己的服务名、ip、端口信息注册到Eureka Server上,Eureka Client进行服务调用时就会从Eureka Server上拉取服务信息。
-
Eureka Client调用某个具有多个实例服务时,应该从众多服务中进行选择,Ribbon提供服务的负载均衡,Ribbon内置了一些负载均衡算法(轮询、随机等),用户也可以自定义算法。
-
SpringCloud的服务调用可以直接通过自行封装Http发送请求,但是每次服务调用都需要大量代码去封装发送和解释返回结果。Java都推崇面向接口编程,使用Feign发送远程请求就像SpringMVC的前端请求后端一样简单,原理如下
- 在启动时Feign会对使用了@FeignClient注解的接口进行扫描生成动态代理类注册到Spring容器中。
- 然后当调用Feign中的接口时,代理类根据接口上的@RequestMapping等注解,来动态封装HTTP请求,发送请求
- 请求结果返回后,代理类会对结果进行解码返回给调用者
-
当某个服务在被调用时发生网络故障或者宕机时,服务调用者由于等不到响应会阻塞直到超时,如果有很多服务调用该服务那么所有的服务都将被阻塞。Hystrix会为每个服务提供独立的线程池,服务调用先打到Hystrix中,某个服务发生故障不会影响到其它服务调用,并且Hystix提供服务降级功能,某个服务挂掉时Hystix可以通过fallback直接构造返回结果,并且处理失败结果,比如说将失败信息保存起来以便进行恢复。
-
随着服务的增多,几十个、几百个甚至是几千个服务,每次调用服务都需要记住服务名。在前后端分离开发的应用中,前端工程师就需要知道每个服务名,这是不切实际的。所有的服务通过zuul配置路径后,发送的请求都通过zuul向服务转发,实现服务访问统一管理。zuul还可以实现统一服务降级、身份权限认证、限流等功能
eureka原理
- 每一个微服务启动的时候,会将自己注册到eureka server上
- 当A服务需要调用B服务时,需要从eureka服务端获取B服务的服务列表,然后把列表缓存到本地,然后根据ribbon的客户端负载均衡规则,从服务列表中取到一个B服务,然后去调用此B服务;当A服务下次再此调用B服务时,如果发现本地已经存储了B的服务列表,就不需要再从eureka服务端获取B服务列表了
- 微服务,默认每30秒,就会从eureka服务端获取一次最新的服务列表,如果某台微服务down机,或者添加了几台机器,此时eureka server会通知订阅他的客户端,并让客户端更新服务列表,而且还会通知其他eureka server更新此信息
- 微服务每30秒向eureka server发送心跳,
- eureka server若90s之内都没有收到某个客户端的心跳,则认为此服务出了问题,会从注册的服务列表中将其删除,并通知订阅它的客户端更新服务列表,而且还会通知其他eureka server更新此信息
- eureka server保护机制,通过打卡开关,可以让eureka server处于保护状态,主要是用于某eureka server由于网络或其他原因,导致接收不到其他微服务的心跳,此时不能盲目的将其他微服务从服务列表中删除。具体规则:如果一段时间内,85%的服务都没有发送心跳,则此server进入保护状态,此状态下,可以正常接受注册,可以正常提供查询服务,但是不与其他server同步信息,也不会通知订阅它的客户端,这样就不会误杀其他微服务
zookeeper为何能作为注册中心?说下大概实现思路?还有什么常见使用场景?
问题1:
Zookeeper是树形数据结构,类似于文件目录,树是由节点(Znode)组成,每个Znode都拥有唯一路径,数据也会存储在Znode下,利用Zookeeper的节点有分为持久节点、临时节点、监听等特性,能让它作为注册中心。
问题2:
- 每启动一个微服务,就会去zk中注册一个临时子节点,例如:5台订单服务,4台商品服务,5台订单服务在zk中的订单目录下创建的5个临时节点,4台商品服务在zk中的商品目录下创建的4个临时接点
- 每当有一个服务down机,由于是临时接点,此节点会立即被删除,并通知订阅该服务的微服务更新服务列表(zk上有watch,每当有节点更新,都会通知订阅该服务的微服务更新服务列表)
- 每当有一个新的微服务注册进来,就会在对应的目录下创建临时子节点,并通知订阅该服务的微服务更新服务列表(zk上有watch,每当有节点更新,都会通知订阅该服务的微服务更新服务列表)
- 每个微服务30s向zk获取新的服务列表
问题3:可以作为分布式锁
Hystrix原理
Hystrix [hɪst’rɪks]的中文含义是豪猪, 因其背上长满了刺,而拥有自我保护能力. Netflix的 Hystrix 是一个帮助解决分布式系统交互时超时处理和容错的类库, 它同样拥有保护系统的能力.
Hystrix的设计原则包括:资源隔离
、熔断器
、命令模式
- 资源隔离:Hystrix通过将每个依赖服务分配独立的线程池进行资源隔离, 从而避免服务雪崩.
- 熔断器:服务的健康状况 = 请求失败数 / 请求总数.熔断器开关由关闭到打开的状态转换是通过当前服务健康状况和设定阈值比较决定的.熔断器的开关能保证服务调用者在调用异常服务时, 快速返回结果, 避免大量的同步等待. 并且熔断器能在一段时间后继续侦测请求执行结果, 提供恢复服务调用的可能.
- 当熔断器开关关闭时, 请求被允许通过熔断器. 如果当前健康状况高于设定阈值, 开关继续保持关闭. 如果当前健康状况低于设定阈值, 开关则切换为打开状态.
- 当熔断器开关打开时, 请求被禁止通过.
- 当熔断器开关处于打开状态, 经过一段时间后, 熔断器会自动进入半开状态, 这时熔断器只允许一个请求通过. 当该请求调用成功时, 熔断器恢复到关闭状态. 若该请求失败, 熔断器继续保持打开状态, 接下来的请求被禁止通过.
3.命令模式:Hystrix使用命令模式(继承HystrixCommand类)来包裹具体的服务调用逻辑(run方法), 并在命令模式中添加了服务调用失败后的降级逻辑(getFallback).
参考:防雪崩利器:熔断器 Hystrix 的原理与使用
Hystrix的内部处理逻辑
- 构建Hystrix的Command对象, 调用执行方法.
- Hystrix检查当前服务的熔断器开关是否开启, 若开启, 则执行降级服务getFallback方法.
- 若熔断器开关关闭, 则Hystrix检查当前服务的线程池是否能接收新的请求, 若超过线程池已满, 则执行降级服务getFallback方法.
- 若线程池接受请求, 则Hystrix开始执行服务调用具体逻辑run方法.
- 若服务执行失败, 则执行降级服务getFallback方法, 并将执行结果上报Metrics更新服务健康状况.
- 若服务执行超时, 则执行降级服务getFallback方法, 并将执行结果上报Metrics更新服务健康状况.
- 若服务执行成功, 返回正常结果.
- 若服务降级方法getFallback执行成功, 则返回降级结果.
- 若服务降级方法getFallback执行失败, 则抛出异常.
Zookeeper和Eureka区别
Dubbo服务特点?画出Duboo架构?说明调用关系
Dubbo 架构具有以下几个特点,分别是连通性、健壮性、伸缩性、以及向未来架构的升级性
节点角色说明
节点 | 角色说明 |
---|---|
Provider | 暴露服务的服务提供方 |
Consumer | 调用远程服务的服务消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 统计服务的调用次数和调用时间的监控中心 |
Container | 服务运行容器 |
调用关系说明
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
dubbo 提供了哪三个关键功能
- 基于接口的远程调用
- 容错和负载均衡
- 自动服务注册和发现
Dubbo中zookeeper做注册中心,如果注册中心集群都挂掉,发布者和订阅者之间还能通信么?
可以的,启动dubbo时,消费者会从zk拉取注册的生产者的地址接口等数据,缓存在本地。每次调用时,按照本地存储的地址进行调用
dubbo 配置 优先级
- 上图中以timeout为例,显示了配置的查找(优先级)顺序,其它retries, loadbalance, actives等类似。
方法级优先,接口级次之,全局配置再次之。
如果级别一样,则消费方优先,提供方次之。 - 其中,服务提供方配置,通过URL经由注册中心传递给消费方。
- 建议由服务提供方设置超时,因为一个方法需要执行多长时间,服务提供方更清楚,如果一个消费方同时引用多个服务,就不需要关心每个服务的超时设置。
参考:dubbo 配置 优先级
dubbo配置覆盖策略
- JVM启动-D参数优先,这样可以使用户在部署和启动时进行参数重写,比如在启动时需改变协议的端口。
- XML次之,如果在XML中有配置,则dubbo.properties中的相应配置项无效。
- Properties最后,相当于缺省值,只有XML没有配置时,dubbo.properties的相应配置项才会生效,通常用于共享公共配置,比如应用名。
参考:dubbo 配置 优先级
Dubbo配置来源有几种?分别是?
4种
- JVM System Properties. -D参数
- Externalized Configuration.外部化配置
- ServiceConfig. ReferenceConfig等编程接口采集的配置
- 本地配置文件dubbo.properties
如何禁用某个服务的启动检查?
<dubbo:reference interface = “com.foo.BarService” check = “false” />
开发测试环境如何绕过注册中心?
- xml:<dubbo:reference id=“xxxService” interface=“com.alibaba.xxx.XxxService” url=“dubbo://localhost:20890”/>
- -D: java -Dcom.alibaba.xxx.XxxService=dubbo//localhost:20890
- .properties: java -Ddubbo.resolve.file=xxx.properties com.alibaba.xxx.XxxService=dubbo://localhost:20890
dubbo 使用什么通信框架?
netty
dubbo默认的序列化框架?你还知道哪些?
- dubbo默认hessian2
- rmi默认为java
- http默认为json
一个服务有多重实现时,如何处理?
可以用group分组.服务提供方和消费放都指定同一个group。
Dubbo服务调用默认是阻塞的?还有其他的?
- 默认是同步等待结果阻塞的、同时也支持异步调用。
- Dubbo是基于NIO的非阻塞实现并行调用. 客户端不需要启动多线程即可完成并行调用多个远程服
务、相对多线程开销较小、异步调用会返回一个Future对象。
Dubbo服务追踪解决方案?
- Zipkin
- Pinpoint
- Sky Walking
4. 设计模式
静态代理、JDK动态代理、CGLIB动态代理的区别
代理方式 | 特点 | 缺点 |
---|---|---|
静态代理 | 需要定义父类或者接口,代理对象和被代理对象需要同时继承父类或者实现该接口,一次代理一个类 | 随着代理类增多,出现大量重复代码,难维护,造成类膨胀 |
jdk动态代理 | 目标类需要实现至少一个接口,代理对象通过JAVA的API动态生成,可以代理一个借口的多个实现 | 只能够代理实现了接口的目标类 |
cglib动态代理 | 代理类要实现MethodInterceptor接口,通过Enhancer创建目标类的子类为代理对象,所有也是通过继承关系创建代理类的,然后通过实现intercept(Object o, Method method, Object[] objects, MethodProxy proxy)方法对所有的方法进行拦截,添加增强处理,注意该方法中要通过代理类的invokeSuper调用父类的方法 | 不能代理final修饰的类 |
设计模式-策略模式
- 解决问题:在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。
- 概念:在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。
- 角色:Context: 环境类、Strategy: 抽象策略类、ConcreteStrategy: 具体策略类
- 代码
- 抽象策略类Sort
public interface Sort { void sort(); }
- 具体策略类ConcreteSort1
public class ConcreteSort1 implements Sort { @Override public void sort() { System.out.println("使用快速排序"); } }
- 具体策略类ConcreteSort2
public class ConcreteSort2 implements Sort { @Override public void sort() { System.out.println("使用归并排序"); } }
- 定义Context环境类
public class Context { public AbstractSort method; public Context(AbstractSort abstractSort) { this.method = abstractSort; } public void contextSort() { method.sort(); } }
- 客户端类Main
public class Main { public static void main(String[] args) { //传入不同的具体策略即可 Context context = new Context(new ConcreteSort2()); context.contextSort(); } }
- 抽象策略类Sort
- 在JDK中的使用:ThreadPoolExecutor中的四种拒绝策略
- AbortPolicy:直接抛出异常。
- CallerRunsPolicy:只用调用者所在线程来运行任务。
- DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
- DiscardPolicy:不处理,丢弃掉。
谈谈Redis缓存穿透、缓存击穿、缓存雪崩
-
缓存穿透:同一时间客户端大量的请求在Redis和数据库中都不存在的数据会导致每次请求都会查DB。解决办法:
- 将从DB查询出来的空值进行缓存“null”,需要设置较短的过期时间
- 使用布隆过滤器。(有一定的误判率,谷歌guava的默认误判率为0.03)
-
缓存击穿:缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
- 设置热点数据永远不过期。
- 加互斥锁,互斥锁参考代码如下:
-
缓存雪崩:大量的key在同一时间过期,同一时间收到大量的请求,流量会直接到达DB,造成DB宕机。解决办法:
- 设置热点数据key永不过期
- 设置过期时间不要集中在一起
6. WEB
幂等性
HTTP/1.1中对幂等性的定义是:一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外)。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。
参考:深入理解幂等性
一次web请求的过程
- 在web客户端中输入网址
- web客户端通过DNS将域名解析成ip
- 根据ip在互联网上找到对应的服务器,建立tcp连接
- web客户端向服务器发起http请求,获取服务器资源。一般在客户端和服务器间会使用Nginx进行请求转发和静态资源处理
- 应用服务器处理接收到的请求,进行业务处理,将处理结果返回给web客户端
- web客户端服务器断开连接(Http1.1之后是长连接,不一定是请求完成后就断开连接,这取决于服务器的操作。)
- web客户端拿到请求结果后进行界面渲染
XSS(Cross site scripting)跨站脚本攻击
为了不与CSS重名故简写为XSS。攻击者在网页嵌入恶意脚本程序,用户打开网页时便在客户端浏览器上执行,以盗取cookie、用户名和密码,下载病毒甚至是获取admin权限。
例如
:用户在input输入的内容是:
<script>alert("哈哈")</script>
用户查看界面时就会出现以下代码:
<body>
<script>alert("哈哈")</script>
</body>
防范
:对特殊字符进行转义
html字符 | 转以后字符 |
---|---|
> | < |
< | > |
’ | & |
" | " |
CRFS(Cross site request forgery)跨站请求伪造
听起来和XSS
相似,但是有很大区别。XSS
是利用站立信任用户,而CRFS
是通过伪装来自信任用户的请求来利用受信任的网站。可以这么理解:攻击者盗用了你的身份,然后向第三方网站发送恶意请求,包括:发邮件、短信、交易转账等。
受害者只需要完成以下两件事,就能被攻击:
- 登录信任站点A,并在本地生成cookie
- 不登出站点A(或者不清除cookie),访问恶意站点B
防范
- 将cookie设置为
HttpOnly
,java设置方法如下:
response.setHeader("Set-Cookie","cookiename=cookievalue;HttpOnly");
- 增加token
- 通过Referer识别:Http协议头中有个字段Referer,记录了请求来源网站。通常,访问一个安全受限的请求都来自于同一个网站。只需要验证请求中的Referer值即可,获取方法:
String referer=request.getHeader("Referer");
TCP/IP组成或者模型
- 应用层(细分为应用层、表示层、会话层):定义数据格式并按照对应的格式解读数据
- 传输层:定义端口,标识应用程序身份,实现端口到端口的通信
- 网络层:定义网络地址、区分网段、子网内MAC寻址、对于不同子网的数据包进行路由
- 网络接口层(细分为物理层、数据链路层):对电信号进行分组并形成具有特定意义的数据帧,然后以广播的形式通过物理介质发送给接收方。
参考:TCP/IP协议
TCP和UDP
- TCP面向连接;UDP是一种无连接协议
- TCP提供可靠的服务,使用阻塞控制和流量控制;UDP不建立连接,不关心消息是否被接受到,无视网络状况数据以恒定速率想发就发,无法提供可靠的服务,不使用阻塞控制和流量控制
- TCP1对1通信;UDP支持1对1,1对多,多对1,多对多通信
- TCP面向字节流;UDP面向报文
- TCP慢;UDP快
- TCP适用于可靠传输的应用(文件传输);UDP适用于实时应用(IP电话、视频会议、直播等)
TCP三次握手
- 连接建立通常由客户端首先发起,客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。
- 服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。
- 客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。
- SYN(Synchronize Sequence Numbers):同步序列编号
- SEQ:初始序号
为何是三次握手?
“A:喂,你听得到吗?”
“B:我听得到呀,你听得到我吗?”
“A:我能听到你,今天 balabala……”
TCP四次挥手
- 客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送
- 服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号
- 服务器B数据发送完毕后,发送一个FIN给客户端A,关闭与客户端A的连接
- 客户端A发回ACK报文确认,并将确认序号设置为收到序号加1
每一端都能主动关闭 这个连接(即首先发送SYN),一般由客户端决定何时终止连接,我们能改变上 边的标识,将左方定为服务器,右方定为客户,一切仍将像显示的一样工作
TCP为什么要四次挥手三次不行吗
- 解释1:这是由于TCP的半关闭造成的,因为TCP连接是全双工的(即数据可在两个方向上同时传递)所以进行关闭时每个方向上都要单独进行关闭,这个单方向的关闭就叫半关闭.关闭的方法是一方完成它的数据传输后,就发送一个FIN来向另一方通告将要终止这个方向的连接.当一端收到一个FIN,它必须通知应用层TCP连接已终止了这个方向的数据传送,发送FIN通常是应用层进行关闭的结果
- 解释2:虽然服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
个人更加偏向解释2
TCP沾包
只有TCP有粘包现象,UDP永远不会粘包,因为TCP是基于数据流的协议,而UDP是基于数据报的协议。发送端可以是一K一K地发送数据,而接收端的应用程序可以两K两K地提走数据,或者一次只提走几个字节的数据,也就是说,应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因。所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
沾包的两种情况
- 发送方:TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据negal优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。
- 接收方:接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
如何处理粘包现象?
1.发送方: 对于发送方造成的粘包问题,可以通过关闭Nagle算法来解决,使用TCP_NODELAY选项来关闭算法。
2. 接收方:接收方没有办法来处理粘包现象,只能将问题交给应用层来处理。
3. 应用层:循环处理,应用程序从接收缓存中读取分组时,读完一条数据,就应该循环读取下一条数据,直到所有数据都被处理完成,但是如何判断每条数据的长度呢?
- 格式化数据:每条数据有固定的格式(开始符,结束符),这种方法简单易行,但是选择开始符和结束符时一定要确保每条数据的内部不包含开始符和结束符。
- 发送长度:发送每条数据时,将数据的长度一并发送,例如规定数据的前4位是数据的长度,应用层在处理时可以根据长度来判断每个分组的开始和结束位置。
TCP能发空消息吗?UDP能发空消息吗
tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头
UDP为什么不会沾包
UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,不会使用块的合并优化算法, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
SSL TLS建立连接过程
特别解释:
- 第二步:服务端向客户端发送公开密钥证书,服务端有一套公钥秘钥假如叫做:PK、SK,CA机构也有一套假如叫做CPK、CSK,此时发送的公开秘钥其实是服务器公钥和CA秘钥加密的结果就是证书,证书=f(PK,CSK)
- 第五步客户端验证证书,其中有一步就是获取服务端公钥,就需要去CA机构获取CPK来解密证书,这个过程可能被攻击拦截,所以客户端内置了很多的CA机构公钥CPK。拿到证书后,直接使用CPK进行解密,得到服务端PK。
加密方式
1. 对称加密
a. k:加密的key
b. f():加密算法
c. data:加密对象
d. x:加密后密文数据
使用k对数据data进行加密,表示为:f(k,data)=x;再次使用k对密文x进行解密,就会得到原始数据data,表示为:f(pk,y)=data;
2. 非对称加密
a. pk:公钥
b. sk:私钥
c. f():加密算法
d. data:加密对象
e. y:加密后密文数据
如果使用公钥pk对data进行加密,表示为:f(pk,data)=y,那么使用私钥sk就可以对密文y进行解密,表示为:f(sk,y)=data;如果使用秘钥sk对data进行加密,表示为:f(sk,data)=y,那么使用公钥pk就可以对密文y进行解密,表示为:f(pk,y)=data;