Redis是一个高性能的内存数据库,以key-value方式存储数据,可以作为缓存使用。
为什么持久化?Redis属于内存式数据库,程序关闭后数据会清空,有时候需要将内存中的数据长期在文件保存起来
持久化策略
AOF:默认每秒对数据进行持久化
RDB:按条件触发持久化操作(任意一个)
900 1 900秒中修改1次
300 10 300秒中修改10次
60 10000 60秒中修改10000次
配置方法
RDB
AOF
appendonly yes / no yes开启AOF
appendfsync everysec 每秒保存
如何选择?
允许少量数据丢失,性能比较高----RDB
只允许很少数据丢失----AOF
不允许数据丢失----RDB + AOF
一对一模型:一个生产者发送消息到一个队列,一个消费者从队列中取消息。
工作队列:工作队列,生产者将消息分发给多个消费者,如果生产者生产了100条消息,消费者1消费50条,消费者2消费50条。
生产者
发布/订阅:发布/订阅模式和Work模式的区别是:Work模式只存在一个队列,多个消费者共同消费一个队列中的消息;而发布订阅模式存在多个队列,不同的消费者可以从各自的队列中处理完全相同的消息。
路由:路由模式的消息队列可以给队列绑定不同的key,生产者发送消息时,给消息设置不同的key,这样交换机在分发消息时,可以让消息路由到key匹配的队列中。
可以想象上图是一个日志处理系统,C1可以处理error日志消息,C2可以处理info\error\warining类型的日志消息,使用路由模式就很容易实现了。
主题:主题模式和路由模式差不多,在key中可以加入通配符:
* 匹配任意一个单词 com.* ----> com.hopu com.blb com.baidu
# 匹配.号隔开的多个单词 com.* ---> com.hopu.net com.hopu com.163.xxx.xxx.xxx
左连接:左连接查询,左表的信息全部展示出来,右表只会展示符合搜索条件的信息,不足的地方记为NULL
关键字:LEFT JOIN
右连接:右连接查询,右表的信息全部展示出来,左表只会展示符合搜索条件的信息,不足的地方记为NULL
关键字:RIGHT JOIN
网关服务对所有请求进行过滤,看请求头中是否携带token,携带的话进行校验,校验成功放行,未携带token或校验失败跳转到登录页面,登录成功后使用RSA+JWT生成token,将token返回到前端页面,vue中main文件中添加全局过滤,给所有请求添加token到请求头中。
自动配置类是什么时候导入内存的 启动类上的注解:@SpringBootApplication
包含三个注解:
1.@ComponentScan 包的扫描
2.@SpringBootConfiguration 定义配置类
3.@EnableAutoConfiguration 启动自动配置(将自动配置类加载到内存)
怎么加载类到内存中? Class.forName("完整类名")
@EnableAutoConfiguration --> AutoConfigurationImportSelector --> loadFactoryNames --->
loadSpringFactories --> classLoader.getResources("META-INF/spring.factories")
在spring-boot-autoconfigure包下保存大量的自动配置类
类名都保存 META-INFO/spring.factotiries文件下
自动配置类生效需要一定条件,如@ConditionalOnClass,引入某些类时(导入了对应的框架依赖)
以Cookie作为凭证媒介
最简单的单点登录实现方式,是使用cookie作为媒介,存放用户凭证。 用户登录父应用之后,应用返回一个加密的cookie,当用户访问子应用的时候,携带上这个cookie,授权应用解密cookie并进行校验,校验通过则登录当前用户。
不难发现以上方式把信任存储在客户端的Cookie中,这种方式很容易令人质疑:
Cookie不安全
不能跨域实现免登
对于第一个问题,通过加密Cookie可以保证安全性,当然这是在源代码不泄露的前提下。如果Cookie的加密算法泄露,攻击者通过伪造Cookie则可以伪造特定用户身份,这是很危险的。 对于第二个问题,更是硬伤。
通过JSONP实现
对于跨域问题,可以使用JSONP实现。 用户在父应用中登录后,跟Session匹配的Cookie会存到客户端中,当用户需要登录子应用的时候,授权应用访问父应用提供的JSONP接口,并在请求中带上父应用域名下的Cookie,父应用接收到请求,验证用户的登录状态,返回加密的信息,子应用通过解析返回来的加密信息来验证用户,如果通过验证则登录用户。
这种方式虽然能解决跨域问题,但是安全性其实跟把信任存储到Cookie是差不多的。如果一旦加密算法泄露了,攻击者可以在本地建立一个实现了登录接口的假冒父应用,通过绑定Host来把子应用发起的请求指向本地的假冒父应用,并作出回应。 因为攻击者完全可以按照加密算法来伪造响应请求,子应用接收到这个响应之后一样可以通过验证,并且登录特定用户。
通过页面重定向的方式
最后一种介绍的方式,是通过父应用和子应用来回重定向中进行通信,实现信息的安全传递。 父应用提供一个GET方式的登录接口,用户通过子应用重定向连接的方式访问这个接口,如果用户还没有登录,则返回一个的登录页面,用户输入账号密码进行登录。如果用户已经登录了,则生成加密的Token,并且重定向到子应用提供的验证Token的接口,通过解密和校验之后,子应用登录当前用户。
这种方式较前面两种方式,接解决了上面两种方法暴露出来的安全性问题和跨域的问题,但是并没有前面两种方式方便。 安全与方便,本来就是一对矛盾。
使用独立登录系统
一般说来,大型应用会把授权的逻辑与用户信息的相关逻辑独立成一个应用,称为用户中心。 用户中心不处理业务逻辑,只是处理用户信息的管理以及授权给第三方应用。第三方应用需要登录的时候,则把用户的登录请求转发给用户中心进行处理,用户处理完毕返回凭证,第三方应用验证凭证,通过后就登录用户。
Eureka(nacos类似):Eureka service:注册中心,里面有一个注表,保Z了各个服务所在的机器和端口号,Eurake Client:负责将这个服务的信息注册到Eureka Server中。
Feign(OpenFeign类似):声明式客户端。
Ribbon:负载均衡,会帮你在每一次请求的时候选择一台机器,均匀的把请求发送到各个机器上。
Hystrix:隔离、熔断以及降级的一个框架。啥意思说白了就是Hystrix会搞很多小线程池,每个小线程池里的线程仅用去请求的服务。
默认spring事务只在发生未被捕获的 runtimeexcetpion时才回滚。
spring aop 异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获runtimeexception的异常,但可以通过
配置来捕获特定的异常并回滚
换句话说在service的方法中不使用try catch 或者在catch中最后加上throw new runtimeexcetpion(),这样程序异常时才能被aop捕获进而回滚
解决方案:
方案1.例如service层处理事务,那么service中的方法中不做异常捕获,或者在catch语句中最后增加throw new RuntimeException()语句,以便让aop捕获异常再去回滚,并且在service上层(webservice客户端,view层action)要继续捕获这个异常并处理
方案2.在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常
IK分词器:
Ik分词器就是一款中文分词器。
分词:把一段文字划分成一个个关键字,我们在搜索的时候会对自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作。
默认的中文分词会将每个字看成一个词,比如“我是天才“会被分成 我、是、天、才,这显然是不符合要求的,可能我们需要分成 我、是、天才、我是天才,所以我们需要安装Ik分词器来解决这个问题。
如果要对中文进行分词,建议使用Ik分词器。
IK分词器提供了两个分词算法。
ik_smart :最少切分。
ik_max_word:最细粒度切分。
1)获得字符串长度
int length()
2) 对字符串进行查找
int indexOf(子字符串) 在字符串中查找字符串出现的位置,没有返回-1
int lastIndexOf(子字符串) 在字符串中查找字符串最后出现的位置,没有返回-1
3)字符串的比较
boolean equals(Object obj) 比较字符串是否相等
boolean equalsIgnoreCase(Object obj) 忽略大小写,比较字符串是否相等
boolean startsWith(String str) 判断字符串是否以该字符串开头
boolean endsWith(String str) 判断字符串是否以该字符串结尾
int compareTo(String str) 将当前字符串和其它字符串比大小,0相等,正数当前字符串大,负数其它字符串大
4)字符串的截取
String substring(int start,int end) 对字符串进行截取
String substring(int start) 从某个位置截取到末尾
char charAt(int index) 获得某个位置的字符
5) 字符串的替换
String replace(String oldString,String newString) 将字符串中某些字符串替换为新的字符串
6) 字符串的分割
String[] split(String c) 按某个字符对字符串进行分割
注意:分割的字符串可以添加\\ 避免特殊符号出现错误
7) 字符串的转换
String toUpperCase() 将字符串转为大写
String toLowerCase() 将字符串转为小写
String trim() 去掉字符串前后所有的空格
8)字符串的连接 String ss = String.join(" ", strs); System.out.println("连接后的字符串:" + ss);
Java中的属性(property),通常可以理解为get和set方法,而字段(field),通常叫做“类成员”,或 “类成员变量”,有时也叫“域”,理解为“数据成员”,用来承载数据的。
1、首先制定项目计划,最初计划是里程碑性质的。可以先按瀑布模型设置,里程碑点主要为需求评审、设计评审、经过代码开发和单元测试后进行集成测试、部署上线是一个很重要的里程碑,一般用户会期望系统何时能使用,进入试运行期。
2、需求开发阶段:怎么样写好需求很关键,这个需要实践经验锻炼自己。如果有项目成员,可以一起做需求,这个阶段对于业务理解、分析、如何开展调研以及文字表述、业务流程图描述还有文档编辑能力都有不少要求。一般分为《用户需求说明书》和《需求规格说明书》,小项目可以写一个《需求分析报告》,《用户需求说明书》是用用户的语言进行描述,让用户和开发团队对于需求的达成一致的理解,《需求规格说明书》,则是对用户需求的分析,形成系统要具有的功能,这个是真正提供用户可交互操作的文档,也就是后期设计和代码开发的重要基线。
另外,作为了解需求,拿出用户UI和用户交流也是一项比较重要的需求获取手段,虽然这个属于设计的范畴
3、系统设计阶段:
系统总体架构,结合用户对系统环境、开发语言以及运行的网络硬件等要求,确定开发工具等,对应用系统关系进行架构性设计,通过需求阶段对用户的分析归类,用图的方式描述出用户和各子系统或模块的全局视图,以及和其他系统的关系。也就是搞清楚系统的边界问题。
概要设计中除了高层架构设计,还需要设计网络拓扑图,以及系统部署图。概要设计比较重要的还有就是子系统、模块进行合理的划分。模块的名称很大程度上会成为用户的主要菜单,如何用用户的角度去取比较清楚的子系统和模块是很重要的。
4、代码开发和单元测试阶段:这个阶段一般来说需要改进瀑布模型,类似跌代开发,把模块进行合理划分,把项目总体计划的代码开发测试阶段划分为多个时间段,每个时间段都包括代码开发、单元测试和集成测试,这个阶段还需要对需求变更进行跟踪控制,如果需求有变更,那么要把需求文档、设计文档都重新跟上。跌代开发的好处就是不让代码开发阶段拉的过程,没有进行及时的自我检查,不小心到了提交时间,却不是用户想要的,还有可能都不是自己想要的。
项目经理重要的责任是控制好进度,能及早发现风险,并能拿出好的预防和解决办法的措施。合理安排好开发团队的任务,合时的任务安排和衔接,你会觉得非常有艺术感,这个要自己体会了。另外,关注项目团队各人员的状况,保持高的战斗力,及时发现并能鼓励团队共同朝一个目标前进。
5、测试工作,测试是项目的很重要的环节,怎么测试,怎么准确测试,怎么有效测试,怎么覆盖测试,时间、人手、经验扽个方面都会有制约。高级测试人员能够分析系统各测试要点,在需求、设计阶段都要参与,提早了解如何去测试,能写出测试用例。
6、文档工作,文档在项目开发中也占有重要位置,除非你觉得代码是项目唯一的成果,那么你把文档抛掉吧,什么都在你的脑子里,团队中人员一走,项目的一部分也就带走了。代码开发其实也需要文档,代码是成果,代码注释是成果,模块开发卷宗也是重要的成果,因为程序员在开发时候的逻辑是怎么样的,对于今后查问题很有作用。除非你的系统设计程度到了方法、类,把代码逻辑也都设计好了,那么程序员就CODEING去吧。
7、QA是对项目过程的质量保障,有些公司吧QA和测试工作合成一个岗位叫做QA&测试人员,或者就叫QA人员。QA是对项目全过程的监管,独立于项目之外。监督项目经理在各项目里程碑提交相关成果,入库形成基线。
1.把内存中的对象保存到一个文件中或者数据库中;
2. 网络上传送对象;
3.通过RMI传输对象;(RMI->Remote Method Invocation 远程方法调用)
小锦囊: RMI是什么?
RMI(Remote Method Invocation,远程方法调用)是bai用Java在JDK1.1中实现的,它du大大增强了Java开发分布式应用zhi的能力。Java作为一种风靡一dao时的网络开发语言,其巨大的威力就体现在它强大的开发分布式网络应用的能力上,而RMI就是开发百分之百纯Java的网络分布式应用系统的核心解决方案之一。其实它可以被看作是RPC的Java版本。但是传统RPC并不能很好地应用于分布式对象系统。而Java RMI 则支持存储于不同地址空间的程序级对象之间彼此进行通信,实现远程对象之间的无缝远程调用。
从页面接收参数:
通过List集合进行参数绑定
传输数据到前台:
1.什么是事件委托?
事件委托也称之为事件代理(Event Delegation)。是JavaScript中常用绑定事件的常用技巧。顾名思义,“事件代理”即是把原本需要绑定在子元素的响应事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。
举个通俗的例子:比如一个宿舍的同学同时快递到了,一种方法就是他们一个个去领取,还有一种方法就是把这件事情委托给宿舍长,让一个人出去拿好所有快递,然后再根据收件人一 一分发给每个宿舍同学;
在这里,取快递就是一个事件,每个同学指的是需要响应事件的 DOM 元素,而出去统一领取快递的宿舍长就是代理的元素,所以真正绑定事件的是这个元素,按照收件人分发快递的过程就是在事件执行中,需要判断当前响应的事件应该匹配到被代理元素中的哪一个或者哪几个。
一个事件触发后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段。
(1)捕获阶段:从window对象传导到目标节点(上层传到底层)称为“捕获阶段”(capture phase),捕获阶段不会响应任何事件;
(2)目标阶段:在目标节点上触发,称为“目标阶段”
(3)冒泡阶段:从目标节点传导回window对象(从底层传回上层),称为“冒泡阶段”(bubbling phase)。事件代理即是利用事件冒泡的机制把里层所需要响应的事件绑定到外层。
事件委托的优点:
【1】可以大量节省内存占用,减少事件注册,比如在ul上代理所有li的click事件就非常棒
<ul id="list">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
......
<li>item n</li>
</ul>
// ...... 代表中间还有未知数个 li
如上面代码所示,如果给每个li列表项都绑定一个函数,那对内存的消耗是非常大的,因此较好的解决办法就是将li元素的点击事件绑定到它的父元素ul身上,执行事件的时候再去匹配判断目标元素。
【2】可以实现当新增子对象时无需再次对其绑定(动态绑定事件)
假设上述的例子中列表项li就几个,我们给每个列表项都绑定了事件;
在很多时候,我们需要通过 AJAX 或者用户操作动态的增加或者删除列表项li元素,那么在每一次改变的时候都需要重新给新增的元素绑定事件,给即将删去的元素解绑事件;
如果用了事件委托就没有这种麻烦了,因为事件是绑定在父层的,和目标元素的增减是没有关系的,执行到目标元素是在真正响应执行事件函数的过程中去匹配的;所以使用事件在动态绑定事件的情况下是可以减少很多重复工作的。
使用事件委托注意事项:使用“事件委托”时,并不是说把事件委托给的元素越靠近顶层就越好。事件冒泡的过程也需要耗时,越靠近顶层,事件的”事件传播链”越长,也就越耗时。如果DOM嵌套结构很深,事件冒泡通过大量祖先元素会导致性能损失。
2.怎么阻止默认动作?
有一些html元素默认的行为,比如说a标签,点击后有跳转动作;form表单中的submit类型的input有一个默认提交跳转事件;reset类型的input有重置表单行为。
如果你想阻止这些浏览器默认行为,JavaScript为你提供了方法。
如下代码:
var a.onclick = function(e){
alert("跳转动作被我阻止了")
e.preventDefault();
//return false;//也可以
}
默认事件没有了。
既然return false 和 e.preventDefault()都是一样的效果,那它们有区别吗?当然有。
仅仅是在HTML事件属性 和 DOM0级事件处理方法中,才能通过返回 return false 的形式组织事件宿主的默认行为。
3.怎么阻止冒泡事件
function stopBubble(e){
if(e&&e.stopPropagation){//非IE
e.stopPropagation();
}
else{//IE
window.event.cancelBubble=true;
}
}
存储过程是一组予编译的SQL语句
它的优点:
1.允许模块化程序设计,就是说只需要创建一次过程,以后在程序中就可以调用该过程任意次。
2.允许更快执行,如果某操作需要执行大量SQL语句或重复执行,存储过程比SQL语句执行的要快。
3.减少网络流量,例如一个需要数百行的SQL代码的操作有一条执行语句完成,不需要在网络中发送数百行代码。
4.更好的安全机制,对于没有权限执行存储过程的用户,也可授权他们执行存储
Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单, 它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解。Feign也支持可拔插式的编码器和解码器。SpringCloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。
1)Feign可帮助我们更加便捷,优雅的调用HTTP API。
2)在SpringCloud中,使用Feign非常简单——创建一个接口,并在接口上添加一些注解,代码就完成了。
3)Feign支持多种注解,例如Feign自带的注解或者JAX-RS注解等。
4)SpringCloud对Feign进行了增强,使Feign支持了SpringMVC注解,并整合了Ribbon和Eureka,从而让Feign的使用更加方便。
1、Eureka Server 启动成功,等待服务端注册。在启动过程中如果配置了集群,集群之间定时通过 Replicate 同步注册表,每个 Eureka Server 都存在独立完整的服务注册表信息
2、Eureka Client 启动时根据配置的 Eureka Server 地址去注册中心注册服务
3、Eureka Client 会每 30s 向 Eureka Server 发送一次心跳请求,证明客户端服务正常
4、当 Eureka Server 90s 内没有收到 Eureka Client 的心跳,注册中心则认为该节点失效,会注销该实例
5、单位时间内 Eureka Server 统计到有大量的 Eureka Client 没有上送心跳,则认为可能为网络异常,进入自我保护机制,不再剔除没有上送心跳的客户端
6、当 Eureka Client 心跳请求恢复正常之后,Eureka Server 自动退出自我保护模式
7、Eureka Client 定时全量或者增量从注册中心获取服务注册表,并且将获取到的信息缓存到本地
8、服务调用时,Eureka Client 会先从本地缓存找寻调取的服务。如果获取不到,先从注册中心刷新注册表,再同步到本地缓存
9、Eureka Client 获取到目标服务器信息,发起服务调用
10、Eureka Client 程序关闭时向 Eureka Server 发送取消请求,Eureka Server 将实例从注册表中删除
Nginx是一款轻量级的Web服务器、反向代理服务器,由于它的内存占用少,启动极快,高并发能力强,在互联网项目中广泛应用。
(1)类型上 从类型上来说,mysql是关系型数据库,redis是缓存数据库
(2)作用上 mysql用于持久化的存储数据到硬盘,功能强大,但是速度较慢 redis用于存储使用较为频繁的数据到缓存中,读取速度快
(3)需求上 mysql和redis因为需求的不同,一般都是配合使用。 补充: redis和mysql要根据具体业务场景去选型 mysql:数据放在磁盘 redis:数据放在内存 redis适合放一些频繁使用,比较热的数据,因为是放在内存中,读写速度都非常快,一般会应用在下面一些场景: 排行榜、计数器、消息队列推送、好友关注、粉丝
1.新建(NEW):创建Thread类的实例时,线程进入新建状态。
2.就绪(RUNNABLE):调用线程对象的start()方法后,等待被分配给CPU时间片、谁先抢到CPU资源,谁开始执行。
3.运行(RUNNING):当就绪的线程被调用获得CPU资源时,进入运行状态,执行run()方法中的功能。
4.堵塞(BLOCKED):线程运行时出现某些原因导致正在运行的线程进入堵塞状态。
5.死亡(DEAD):线程执行完毕或被强行终止。
1.基于数据库实现排他锁
2.基于redis实现
3.基于zookeeper实现
调用对象不同
wait() 由锁对象调用
sleep() 由线程调用
锁使用不同
执行wait后,自动释放锁
执行sleep后,不会释放锁
唤醒机制不同
执行wait后,可以被通知唤醒
执行sleep后,只能等待时间结束后,自动唤醒