读写分离配置:在项目中我们配置了两个数据源,一个主数据库的,一个是从数据库的,在事务配置的时候,我们给从的数据dataSource配置了只读事务,在项目中根据操作判断注入不同的数据源,如果是增删改操作的注入主的数据源,所有的查询操作注入从的数据源,主从同步配置:首先找到主数据库的my.ini文件,在里边配置server-id 每个数据库的server-id不能重复要保证唯一, 配置binlog-do-db指明需要同步的数据库,log-bin来指明日志文件,配置完后,重启主数据库的服务,登陆主数据库,创建一个用于从数据库访问的用户,然后通过show master status;查询出主数据库的状态,记录下它的File和Position的值,紧接着配置从数据库,找到从数据库的my.ini文件,在里边配置server-id,log-bin和replicate-do-db 指明需要同步的数据库,然后重启从数据库的服务,重启完后登陆从数据库,创建从数据库对主数据库的连接, master_log_file要和刚才主数据库查询的File字段内容一致,master_log_pos要和主数据库刚才查出的Position一致, 然后通过slave start,来启动从数据库,这样主从同步就配置好了。
如何降低延迟:
1.主库和同库尽可能在同一个局域网内,交换机网卡采用千兆网卡。
2.主数据库更新完成之后产生的操作日志不是瞬间产生的,我们可以通过设置sync(sen ke)_binlog=1, 让它瞬间产生磁盘日志,(n=1指主数据库只要操作一次,就产生一次磁盘日志,n=10,就是操作10次,产生一次),从数据库可以依赖磁盘日志,瞬间产生同步,可以达到减低延迟的效果。
3. 设置主库和从库读取日志失败之后,及时重新建立连接,延迟缩短。
2. nginx+tomcat集群配置,如何解决session共享问题
首先开启每个tomcat的集群策略,在每个tomcat的service.xml中添加Cluster标签,然后在Engine标签里配置上jvmRoute,每台tomcat的jvmRoute配置的名称要一样,这样也就解决了session共享的问题了,nginx的话打开我们的nginx.conf配置文件,在里边配置集群的负载策略,nginx里边支持轮询、权重、ip_hash等策略,由于我门的服务器在硬件配置上一样,所以我们采了ip_hash策略,ip_hash 也就是说一个用户第一次访问nginx,nginx会将第一分发的请求服务器ip地址记录下来,下次访问的时候,发现之前有过访问,就会将第二次的访问还分发到第一次访问的服务器上。如果刚好分发的服务器宕机了,nginx就会自动给你分配到一个新的服务器上。
1.Tomcat中配jvmRote,jvmRoute配置的名称要一样
2、不使用session,换用cookie
3、session存在数据库(MySQL等)中
4、session存在memcache或者redis中
5.nginx中的ip_hash技术能够将某个ip的请求定向到同一台后端,这样一来这个ip下的某个客户端和某个后端就能建立起稳固的session
JVM 优化主要是解决java的 GC (垃圾回收)问题。
JVM 的使用过程中各代有,年轻带主要存放,新创建对象。 年老代,年老代存放从年轻代存活的 对象。Perm(持久代)用 于存放静态文件,如今Java类、方法等。一般持久代可以设置大一点。
GC优化的目的有两个:
1、将转移到老年代的对象数量降低到最小;
2、减少full GC的执行时间;
为了达到上面的目的,一般地,你需要做的事情有:
1、减少使用全局变量和大对象;
2、调整新生代的大小到最合适;
3、设置老年代的大小为最合适;
4、选择合适的GC收集器;
【垃圾回收(GC收集器):串行收集器、并行收集器、并发收集器。
串行处理器:
–适用情况:数据量比较小(100M左右);单处理器下并且对响应时间无要求的应用。
–缺点:只能用于小型应用
并行处理器
–适用情况:“对吞吐量有高要求”,多CPU、对应用响应时间无要求的中、大型应用。举例:后台处理、科学计算。(例如 ERP 银行系统)
–缺点:应用响应时间可能较长
并发处理器:
–适用情况:“对响应时间有高要求”,多CPU、对应用响应时间有较高要求的中、大型应用。举例:Web服务器/应用服务器、电信交换、集成开发环境。(例如互联网网站)】
5、设置jvm堆大小 ,32bit 1.5-2G ,64bit 可以超过 2G ,新版的JDK 每个线程的堆大小在1M改变这个线程所占用的堆大小,可以生成更多的线程,一般项目里线程数不能超过5000个。
3.1、堆和栈的区别:
JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。
差异:
1.堆内存用来存放由new创建的对象和数组。
2.栈内存用来存放方法或者局部变量等
3.堆是先进先出,后进后出
4.栈是后进先出,先进后出
相同:
1.都是属于Java内存的一种 2.系统都会自动去回收它,但是对于堆内存一般开发人员会自动回收它
3.2、怎么获取 Java 程序使用的内存?堆使用的百分比?
可以通过java.lang.Runtime 类中与内存相关方法来获取剩余的内存,总内存及最大堆内存。通过这些方法你也可以获取到堆使用的百分比及堆内存的剩余空间。Runtime.freeMemory() 方法返回剩余空间的字节数,Runtime.totalMemory() 方法总内存的字节数,Runtime.maxMemory() 返回最大内存的字节数。
数据库优化:
数据库优化吧我觉应该从硬盘、内存和网络带宽考虑,提高硬盘的读写速度,增大带宽提高吞吐量,增大
服务器内存,可以采用读写分离,降低单台数据库的访问压力,查询的时候控制数据量的大小,返回更少
数据,减少交互次数,减少cpu及内存的开销,
sql优化:如果一个表中数据量过大我们可以采用横切割,如果一个表中字段过多,我们可以采用纵切割,适度冗余
减少表关联查询,避免过多的联查,设计合理的表关系,适当建立索引,但是不能每个字段都建立索引,
因为索引也要占用一定的物理存储空间,而且索引也需要动态维护,增加索引虽然可以提高查询速度,但
是如果索引过多就会降低增删改的速度,写sql的时候也要注意,尽可能不要写一些让索引失效的sql,例
如:索引的字段不能为空,不能进项模糊搜索,不能进项逻辑运算,不能使用函数,给索引查询的值应是
已知数据,不能是未知字段值。也可以使用force(fou si)强制走索引,咱们常用的索引包括,单个索引、唯一索引、
复合索引,单个索引就是创建的索引中只包含一个字段,复合索引是创建的一个索引中包含多个字段,
创建索引:create index index_name on table_name(column_name)
怎么防止sql注入
1.永远不要信任用户的输入。对用户的输入进行校验,可以通过正则表达式,或限制长度;对单引号和双"-"进行转换等。
(1).使用PreparedStatement(pu pei er de si de te men ci)
sql注入只对sql语句的准备(编译)过程有破坏作用而PreparedStatement(pu pei er de si de te men ci)已经准备好了,执行阶段只是把输入串作为数据处理, 而不再对sql语句进行解析,准备,因此也就避免了sql注入问题.
(2).使用正则表达式过滤传入的参数
(3).字符串过滤
(4).jsp中调用该函数检查是否包函非法字符
(5).JSP页面判断代码 ”‘”,”\”,”/”
2.永远不要使用动态拼装sql,可以使用参数化的sql或者直接使用存储过程进行数据查询存取。
3.永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
4.不要把机密信息直接存放,加密或者hash(ha 史)掉密码和敏感的信息。
5.应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装
6.sql注入的检测方法一般采取辅助软件或网站平台来检测,软件一般采用sql注入检测工具jsky,网站平台就有亿思网站安全平台检测工具。MDCSOFT SCAN等。采用MDCSOFT-IPS可以有效的防御SQL注入,XSS攻击等。
tomcat优化我们主要从三方面优化,一是内存优化,tomcat内存优化主要是对tomcat启动参数优化,我们可以在 tomcat 的启动脚本catalina.sh 中设置 java_OPTS 参数,通过Xms来初始化虚拟机最小内存Xmx来初始化虚拟机可使用的最大内存,
二是并发优化,通过配置server.xml里的Connector标签,在标签里配置maxThreads客户请求最大线程数、minSpareThreads 初始化时创建的socket线程数maxSpareThreads连接器的最大空闲socket线程数,connectionTimeout 连接的超时时间,minProcessors服务器创建时的最小处理线程数,maxProcessors服务器同时最大处理线程数,
三是缓存优化,配置compression打开压缩功能
poi在3.8版本后开始支持大数据的导出,首先new一个SXSSFWorkbook,并设置内存中最多放5000条数据,之后的数据poi会自己写成临时文件存到到服务器的物理硬盘中,通过线程池将所有数据导完后会自动合成一个excel文件,然后调用workorkbook.dispose();清除服务器上的poi临时文件,然后我们通过IO流将这个excel响应给浏览器下载,最后将服务器上的excel删除掉。
poi导出结合多线程使用
当时在使用线程池的时候呢我们是将常用的四个线程池封装作为一个工具类来使用的,在做导出数据的时候虽然封装了四个线程池,但是我们用的是定长线程池newFixedThreadPool,并且设置次线程池中线程的数量为50,同时创建了一个用户信息导出的线程类用来实现Runnable接口,传入了service业务处理层对象及用户对象两个私有的全局变量,重写run方法,建了一个有参构造方法,传入了两个参数,分别是service业务处理层对象,及用户对象,之后在run方法中通过service业务处理层对象调用业务处理层接口实现类中的poi导出方法,最后在Controller控制层中通过封装的线程池工具类对象调用FixedThreadPool定长线程池方法在方法中new一个线程实现类将其线程实现类中的方法调用过来返回给前台浏览器进行下载导出。
我们在项目中用redis来做缓存,我们在登陆的时候将用户登陆时获取的验证码存放到了redis中,并设置了验证码的有效期为30分钟,当用户登陆时我们从redis中获取出用户的点击时获取的验证码,如果获取不到直接返回前台,并返回相应的错误码,否则取出redis中的验证码和前台传过来的验证码进行比较,我们还用redis缓存了前台首页的名师、网站统计文章 好文推荐、网站最近30条活跃统计等
由于Redis是一个基于key,value的支持多种数据类型的可进行持久化的内存数据库,是一个服务级的缓存。它支持对象,集合,map,字符串,json,一般存的都是可序列号对象(可序列号的对象是指将对象转化为二进制,反序列号是指将二进制返还成对象,session,application,request为非序列号对象)。所以:
在我们实际项目中 :
在我们的项目中,为了保证redis不会因为占用内存过大而导致系统宕机,通常将redis当做缓存服务器使用,用redis缓存分类列表,品牌列表,热销商品,推荐商品以及该商品的关联商品等,使用jedis作为客户端,考虑到性能问题使用了jedis连接池。并考虑到redis服务器的高可用性,可以做redis的主从复制,通过哨兵来使主服务器宕机时,从服务器自动转化为主服务器继续提供服务(redis集群情况下,多个redis存储的内容通常是一样的,链接的时候,我们只链接主redis,如果主redis挂掉,哨兵会随机选取在从redis中选取其中一个redis成为主redis,继续返回内容)。redis和spring整合之后获取一个模板工具叫redistempelet , get方法取,set方法存,set方法有两种,第一个有两个参数,key 和value, 第二个set方法有三个参数,除了key和value,还有超时(做电商项目的购物车模块)。我们用的时候,做一个if判断, 拿方法名和参数作为key值 ,去redis里面取, 取到了就展示 ,没有的话就存进redis,下次使用的话就直接取。
spring和redis的整合
首先导入jar包,spring-data-redis.jar 和 jedis.jar,然后在redis-conf.properties里配置上redis的链接信息,
然后在spring-redis.xml中加载redis-conf.properties配置文件,紧接着创建一个
JedisConnectionFactory链接redis,然后创建一个redisTemplate,在redisTemplate中配置序列化信息,最后在需要
操作redis的地方注入redisTemplate,通过redisTemplate对redis缓存进行操作。
Redis 的好处
(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
(2) 支持丰富数据类型,支持string,list,set,zset,hash
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
redis在项目中遇到的问题:
1).缓存穿透的问题:
一般出现这样的问题,是因为当我们查询一条肯定不存在的数据的时候,缓存中没有,就会透过缓存来查询数据库,数据库也不存在,这样就会将值保存在缓存中最后还是缓存和数据库中都没有,如果一直访问这条数据。我们就对数据库进行频繁的查询给数据库带来压力
解决办法:当查询的时候,如果缓存和数据库中都没有,我们就将这个数据以空的形式存放在缓存中,(或者是给一个false的标示)这样就不用去数据库就可以知道不存在,减少对数据库查询的次数。当我们这个值发生改变的时候,我们在重新赋值
2.并发情况:
当我们大量访问都是查询一个缓存中没有的数据时,这样就会去数据库中进行查询,可能会造成数据库宕机,
解决办法:在查询的时候,我给它添加一个同步锁,只有第一条数据去,数据库中查询并返回到redis中后才能查询,这是数据库中已近存在了值,这样可以避免
3.雪崩:
大量数据的访问缓存超时,这样用户就会访问到数据库,第一台数据库崩溃了,访问就会到第二台数据库进行查询,这样就会导致额第二台崩溃。
解决办法:就是设置失效时间,不要一起失效,或者是设置在访问少的时候,或者设置永远不失效。
Redis持久化:redis的一大特点就是可以将数据进行持久化,在一定程度上确保了数据的安全性,但不是绝对的;
首先持久化分为rdb(快照持久化)和aof(精细持久化);
快照持久化,是默认开启的;会自动保存数据,当启动时会在文件夹中生成dump(荡 pu).rdb文件;存放持久化后的数据;
当然我们也可以设置持久化的频率,在redis.conf文件中通过save进行设置,默认有三种情况,每秒超过一万数据或每5分钟有10条数据的时候再或者每15分钟有1条记录,都会执行快照持久化,
当然也可以通过bgsave的方法来手动进行一个快照持久化;(也可以通过ip和端口号就给别人进行手动持久化);
如果频繁的快照持久化,会降低性能和效率,
但是这样也出现了一个问题,就是当一分钟内如果有一万条数据时,不会提交,但是在下一次提交之前,停电了,这样就会丢失掉这些数据;
当时想到的解决方法呢就是和(AOF)精细持久化进行一个结合,达到一个秒级的持久化;
这个持久化需要我们手动进行开启,(注意,AOF开启之后,之前的数据都会丢失,所以要在一开始使用时就要配置好)开启的方法就是在配置redis.conf,将appendOnly 改为yes;同时还可以更改文件名称;然后重新启动服务,这时精细化持久化就启动好了
appendfsync always #每次有数据修改发生时都会写入AOF文件。
appendfsync everysec #每秒钟同步一次,该策略为AOF的缺省策略。
appendfsync no #从不同步。高效但是数据不会被持久化。
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。
Redis集群
Redis集群
首先下载linux版本的redis安装包,解压,找到redis的conf文件,配置端口号,配置要开启的数据库,看有几个库,然后再配置redis开启后日志存储的位置,数据的存储位置,支持后台运行模式,在配置下开启集群模式,完成之后,保存,复制几分,然后启动,多启动几台单台服务,一般都是一组一组,我们一般都配置六台,总共三组,所有单台服务启动完毕后,通过redis-trib(踹 bu).rb create --replicas(re pu li kai si) 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
Redis 集群
Redis 在3.0版本前只支持单实例模式,虽然支持主从模式、哨兵模式部署来解决单点故障
Redis 在 3.0 版本以后就推出了集群模式。
Redis 集群搭建规划,由于集群至少需要6个节点(3主3从模式)
我们计划集群中 Redis 节点的端口号为 9001-9006 ,端口号即集群下各实例文件夹。数据存放在 端口号/data 文件夹中。
2.复制执行脚本
在 /usr/local/redis-cluster 下创建 bin 文件夹,用来存放集群运行脚本,并把安装好的 Redis 的 src 路径下的运行脚本拷贝
3.复制一个新 Redis 实例
我们现在从已安装好的 Redis 中复制一个新的实例到 9001 文件夹,并修改 redis.conf 配置。修改 redis.conf 配置和单点唯一区别是下图部分,其余还是常规的这几项:
port 9001(每个节点的端口号)
daemonize yes bind 192.168.119.131(绑定当前机器 IP)
dir /usr/local/redis-cluster/9001/data/(数据文件存放位置)
pidfile /var/run/redis_9001.pid(pid 9001和port要对应)
cluster-enabled yes(启动集群模式)
cluster-config-file nodes9001.conf(9001和port要对应)
cluster-node-timeout 15000
appendonly yes
4. 再复制出五个新 Redis 实例
我们已经完成了一个节点了,其实接下来就是机械化的再完成另外五个节点,其实可以这么做:把 9001 实例 复制到另外五个文件夹中,唯一要修改的就是 redis.conf 中的所有和端口的相关的信息即可,其实就那么四个位置。开始操作,看图:
5. 调用 ruby 命令来进行创建集群,–replicas 1 表示主从复制比例为 1:1,即一个主节点对应一个从节点;然后,默认给我们分配好了每个主节点和对应从节点服务,以及 solt 的大小,因为在 Redis 集群中有且仅有 16383 个 solt ,默认情况会给我们平均分配,当然你可以指定,后续的增减节点也可以重新分配。
Redis缓存失效
1、设置过期时间
expire key time(以秒为单位)–这是最常用的方式
setex(String key, int seconds, String value)–字符串独有的方式
注意:
除了字符串自己独有设置过期时间的方法外,其他方法都需要依靠expire方法来设置时间
如果没有设置时间,那缓存就是永不过期
如果设置了过期时间,之后又想让缓存永不过期,使用persist key
2、三种过期策略
定时删除
含义:在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除
优点:保证内存被尽快释放
缺点:
若过期key很多,删除这些key会占用很多的CPU时间,在CPU时间紧张的情况下,CPU不能把所有的时间用来做要紧的事儿,还需要去花时间删除这些key
定时器的创建耗时,若为每一个设置过期时间的key创建一个定时器(将会有大量的定时器产生),性能影响严重
没人用
惰性删除
含义:key过期的时候不删除,每次从数据库获取key的时候去检查是否过期,若过期,则删除,返回null。
优点:删除操作只发生在从数据库取出key的时候发生,而且只删除当前key,所以对CPU时间的占用是比较少的,而且此时的删除是已经到了非做不可的地步(如果此时还不删除的话,我们就会获取到了已经过期的key了)
缺点:若大量的key在超出超时时间后,很久一段时间内,都没有被获取过,那么可能发生内存泄露(无用的垃圾占用了大量的内存)
定期删除
含义:每隔一段时间执行一次删除过期key操作
优点:
通过限制删除操作的时长和频率,来减少删除操作对CPU时间的占用–处理"定时删除"的缺点
定期删除过期key–处理"惰性删除"的缺点
缺点
在内存友好方面,不如"定时删除"
在CPU时间友好方面,不如"惰性删除"
难点
合理设置删除操作的执行时长(每次删除执行多长时间)和执行频率(每隔多长时间做一次删除)(这个要根据服务器运行情况来定了)
哨兵的详细解释:
哨兵当时害怕的一点,就是说如果我要是这个一redis不够用,如果这个连接数满了,我们这个可能就是不能及时获取数据,所以我配了多个的一个集群,集群一主多从,万一主库一挂就不能更新数据了,所以我们配了个哨兵,Sentinel(三 te nao)能够监控整个这个集群。
同时向所有的这个里边的节点发生心跳,谁要死了就踢出去,主要是死了,直接从里边呢进行一个选举选举的时候,有一个选举算法选出来一个新的一个主,然后整个集群不会崩的,有哨兵的情况下什么时候集群就崩了,当这所有的节点达到一半以上全死的时候,这个集群就崩了。Sentinel(三 te nao)有一个redis-Sentinel(三 te nao)这么一个服务,你把它启动的时候,就类似于启动了一个redis-server一样,你启动它就起来了。它也有配置文件,它配置的时候需要监控的是主节点,主节点一旦被监控到,这个主节点下的所有从节点都会被他监控到。所以这个东西还是比较简单的。
我们主要用mongodb来存储我们项目里面的操作日志和课程评论日志记录的话我们主要是结合aop来使用的,首先我们来配置一个aop的切面类,再给aop的使用规则,哪个类里面的哪个方法使用当前切面类,利用后置通知类获取当前方法的操作日志,将操作日志存储到mongodb,然后进行分类管理查看。利用后置通知传到数据库。评论的话我们做的是一个树表结构,就是用户可以回复用户的评论,主要字段有(评论id,课程id,标题,内容,评论人,评论的发布时间)并且为了提高可用性和高并发用了3台服务器做了mongodb的副本集,其中一台作为主节点,另外两台作为副本节点,这样在任何一台mongodb服务器宕机时就会自动进行故障转移,不会影响应用程序对mongodb的操作,为了减轻主节点的读写压力过大的问题,我还对mongodb副本集做了读写分离,使写操作在主节点进行,读取操作在副本节点进行。为了控制评论,我们的评论的界面设置在了购买状态,只有购买了该课程的人才可以评论
*怎么整合
首先添加spring和mongodb整合的jar包spring-data-commons,在spring的配置文件中引入mongodb的标签头,在配置文件内添加mongo 标签配置链接mongodb数据库的地址和端口号,然后注入mongodb的mongoTemplate模板,在模板里通过constructor-arg ref 指明上面配置好的mongo数据库连接信息,通过constructor-arg name 指明要操作的数据库,最后将我们的mongoTemplate模板注入到我们需要操作mongodb数据的Dao层中。
Redis 和Mongodb 的 区别?
MyISAM不支持事务,每次查询具有原子性,InnoDB支持事务,具有事务提交、回滚和崩溃修复能力,MyISAM只支持表锁,InnoDB支持表锁、行锁、行锁大幅度提高了多用户并发操作的性能。但是InnoDB的行锁,只是在WHERE的主键是有效的,非主键的WHERE都会锁全表的,MyISAM不支持外键,InnoDB支持外键,总的来说,MyISAM和InnoDB各有优劣,各有各的使用环境,但是InnoDB的设计目标是用来处理大容量的数据库系统的,我个人觉得使用InnoDB可以应对更为复杂的情况,特别是对并发的处理要比MyISAM高效
Mybatis的一级缓存是指SqlSession。一级缓存的作用域是一个SqlSession。Mybatis默认开启一级缓存。在同一个SqlSession中,执行相同的查询SQL,第一次会去查询数据库,并写到缓存中;第二次直接从缓存中取。当执行SQL时两次查询中间发生了增删改操作,则一级缓存清空。
Mybatis的二级缓存是指mapper映射文件。二缓存的作用域是同一个namespace下的mapper映射文件内容,多个SqlSession共享。Mybatis需要手动设置启动二级缓存。在同一个namespace下的mapper文件中,执行相同的查询SQL,第一次会去查询数据库,并写到缓存中;第二次直接从缓存中取。当执行SQL时两次查询中间发生了增删改操作,则二级缓存清空。
mybaits的二级缓存是mapper范围级别,要在具体的mapper.xml中开启二级缓存。
在核心配置文件 中加入:
mapper.xml下的sql执行完成会存储到它的缓存区域(HashMap)。
在我平常的工作过程中对spring的应用还是比较多的,整体感觉也没有什么,主要有两个核心AOP和IOC/DI,分别为面向切面编程思想和控制反转/依赖注入,工作过程中主要用到IOC注入这块比较多,通过spring的注入能够更加方便维护bean之间的关系,大多数的bean实例都是单例的,很好的节省的对内存的消耗。Spring的注入方式主要用到过构造函数注入,属性注入,注入数据的类型比较多,常用对象注入,也使用到过list集合注入,使用比较灵活。
ioc是控制反转,主要用来维护bean之间的注入关系,使用工厂模式创建bean的实例,然后再根据xml中配置的bean的注入关系为创建好的实例注入bean对象。
Spring的aop面向切面编程是基于代理模式实现的,通过我的工作经验来看,它其实就是把一部分公用的代码段提取到一个实现类中,让程序在运行过程中把代码再拼接成完整代码执行,增强代码的可读性和高复用性,尽可能的避免重复代码的出现,但是并不是所有的业务都适合使用spring的aop功能。
Spring的aop由切点和通知组成,通知分为前置通知、后置通知和环绕通知,我这块主要用过前置和后置通知。环绕通知这块大概了解过,没有深入的理解,主要把公用代码提取到前置通知和后置通知中,在代码运行过程中先运行前置通知方法,再运行切点方法,最后运行后置通知方法。
注释:java反射技术可以通过类的全限定名创建对应对象,创建对象的方法是newInstance创建对应对象。
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
spring原理这块我也有一定的了解,使用spring这么长时间,我认为spring其实就是项目启动时加载spring的监听器,通过监听器和dom4j等解析xml技术读取spring的xml文件,把spring.xml文件中的所有bean标签读取到,拿到标签中的class属性,通过java的反射技术创建bean的实例,把所有创建好的bean实例放入一个beanMap中,beanMap的key为bean标签的ID值,value为java反射技术创建的具体实例对象,后续需要把创建好的bean注入给别的类使用时,其实就是通过bean的id属性从beanMap中获取对应bean实例,调用set方法把获取出来的bean注入到相关使用的实例中。
事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。
特性:事务是恢复和并发控制的基本单位。
事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。
原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(durability)。指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
我们在项目中是使用spring的AOP来控制事务的,主要配置了事务的传播特性和service业务逻辑层的切面位置,事务的传播特性主要有:
PROPAGATION(pa bu lei gei shen)_REQUIRED(re kuai er de)
支持当前事务,如果当前没有事务,就新建一个事务
PROPAGATION_SUPPORTS(si po er ci)
支持当前事务,如果当前没有事务,就以非事务方式执行
PROPAGATION_MANDATORY(man de te rui)
支持当前事务,如果当前没有事务, 就抛出异常等,我们平常使用PROPAGATION_REQUIRED居多。
spring中事务的传播特性好像有5个左右,我做项目的时候使用最多的就是PROPAGATION_REQUIRED,它所代表的意思支持当前事务,如果当前没有事务,就新建一个事务。
spring中事务的隔离级别有5个,默认使用的是ISOLATION(ai si lei shen)_DEFAULT,
它代表使用数据库默认的事务隔离级别,也是我们项目中最常使用的。除此之外还有:
读未提交:
它充许另外一个事务可以看到这个事务未提交的数据,这种隔离级别会产生脏读,不可重复读和幻像读。
读提交:
保证一个事务修改的数据提交后才能被另外一个事务读取,也是大多数数据库的默认值。可以避免脏读,但会产生不可重复读和幻像读。
重复读:
在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
串行化:
顺序执行事务。除了防止脏读,不可重复读外,还避免了幻像读。并发性也最低,但最安全。
不可重复读的重点是修改 :
同样的条件 , 你读取过的数据 , 再次读取出来发现值不一样了 。
幻读的重点在于新增或者删除 :
同样的条件 , 第 1 次和第 2 次读出来的记录数不一样。
声明式事务: 在spring-common.xml在aop:config里面设定切面为service层引入配置方法的标签tx:advice(这个标签里面配置了使用事物的方法,事物的传播特性,隔离级别等),tx:advice里面又引入了transactionManager,transaction又引入sessionFactory,sessionFactory注入dataSource(数据源/数据库连接池).
什么是分布式事务
简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
分布式事务的产生的原因
2.1、数据库分库分表
当数据库单表一年产生的数据超过1000W,那么就要考虑分库分表,具体分库分表的原理在此不做解释,以后有空详细说,简单的说就是原来的一个数据库变成了多个数据库。这时候,如果一个操作既访问01库,又访问02库,而且要保证数据的一致性,那么就要用到分布式事务。
2.2、应用SOA化
所谓的SOA化,就是业务的服务化。比如原来单机支撑了整个电商网站,现在对整个网站进行拆解,分离出了订单中心、用户中心、库存中心。对于订单中心,有专门的数据库存储订单信息,用户中心也有专门的数据库存储用户信息,库存中心也会有专门的数据库存储库存信息。这时候如果要同时对订单和库存进行操作,那么就会涉及到订单数据库和库存数据库,为了保证数据一致性,就需要用到分布式事务。
@RequestMapping声明方法的具体访问路径,@RequestMapping放在类上时相当于struts2的@nameSpace,就是在具体访问路径之前再加一层目录。
@RequestParam相当于request.getAttribute()方法,从request作用域对象中获取对应的参数值。
@ResponseBody 把返回的参数转换成xml或者json格式,主要与ajax请求结合使用。
这个很简单啊,我们公司用的是nexus这个东西,搭建也比较简单,系统里配上jdk环境变量,然后直接启动nexus服务,由于nexus默认连接的是maven中央库,受限于网速的原因,有时候下载jar包较慢,所以我们在nexus私服上配置第三方代理仓库,开源中国仓库已经停止了,我们配的是阿里云的仓库,nexus创建连接服务有几个选项:公共组、代理、第三方等,配置完阿里云的仓库代理,还要把这个代理添加到公共组中,然后在maven的settings.xml文件中配置mirror镜像,我们就是这么用的。
抽象类不能被实例化。首先我们要知道什么叫实例化,new一个对象并不叫实例化,实例化对象是创建一个对象并给他的属性赋值,抽象类是无参的构造函数,他是可以new来调用的,他里面全是方法,没有属性,我们不能给他赋值,所以说抽象类是不能实例化的后来发帖问了,总结得到:子类实例化会先初始化父类,父类初始化并不是又创建一个父类对象,而是把父类中定义的对象相关属性都初始化,因为这些属性子类对象也是拥有的。所以,为了保证子类对象的完整性,要从最底层的父类开始,逐级初始化,所有初始化都完成后才会生成一个完整的子类对象。(也就是要区分开实例化和初始化)
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。
把Java对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为Java对象的过程称为对象的反序列化。
对象的序列化主要有两种用途:
1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
2) 在网络上传送对象的字节序列。
C/S (Client/Server)结构,就是客户机和服务器结构,常见如咱们现在用的比较多的手机app.
B/S(Browser/Server)结构是浏览器和服务器结构。
管理权限设置这一块,也是我单独负责的,用了大概六张表左右,这个是控制到按钮的操作。四张信息表,这个用户表,角色表,权限表,还有一张是按钮的那张表,有两个关联信息表,用户角色的关联表,还有角色权限的信息表,后台任何一个用户登录之后,会去用户表里边先确认自己的用户信息是否合法。如果合法的话,我们会去用户与角色的中间表里面查这个当中具有的所有角色ID集合,然后查到集合之后,呢我们会拿这个集合去角色与权限这个中间表里,查到当前用户所具有的所有权限的ID这个角色集合,查到这个集合后,把这个角色统一返回,因为在我们的角色权信息表里边有个字段,它这个字段,算是按钮的一个ids。当用户在查到他所有权限集合的时候,同样也能查到他所具有的所有按钮id集合,然后我们会把这些信息呢全部返回到页面去。
权限展示这块呢,我们是用了layui的树形菜单来展示的,按钮这块的话,我们是直接使用了EL标签来控制的。而且我们当时还做了一个拦截器的功能,就是说我们在每次登录的时候会首先来拦截这个url请求。用户的每个请求都会被这个拦截器拦截,如果当前请求是该用户不具备权限的请求,我们会在后台拦截当前用户,然后直接把url给打到登录页面去。
普通5表权限:
当时我们做权限用了6张表,有用户表,角色表,用户角色关联表,权限树表,角色权限关联表,详细菜单表,当用户登陆时我们通过获取session里的用户id,通过5表关联查询出左边的导航树,然后我们配置一个拦截器,当用户发送一个请求时,我们在拦截器里通过request.getRequestURI()获取到用户请求的路径,然后通过用户的id查询出用户所拥有的权限路径列表,判断当前请求地址是否在所用有访问权限的请求列表之内,如果存在则返回true方法这个请求,否则跳转到没有权限的提示页面中。
几张表都能做权限,一张两张三张都可以,不用表也可以,用txt或者任何可持久化的能保存数据的东西
HashMap底层实现是一个线性数组Entry。
HashMap其实也是一个线性的数组实现的,所以可以理解为存储数据的容器就是一个线性数组。
首先HashMap里面实现一个静态内部类Entry,重要的属性有 key , value, next,从属性key,value
我们就能很明显的看出来Entry就是HashMap键值对实现的一个基础bean,所以说到HashMap的基础就是
一个线性数组,Map里面的内容都保存在Entry[]里面。
如果hashmap的key出现hash冲突,如何解决?
利用Entry类的next变量来实现链表,把最新的元素放到链表头,旧的数据则被最新的元素的next变量引用
HashMap不是线程安全的,HashMap是map接口的子类,是将键映射到值的对象,其中键和值都是对象,并且不能包
含重复键,但可以包含重复值。HashMap允许null key和null value,而hashtable不允许。
HashTable是线程安全,HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要
区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上可能高于Hashtable。
HashMap内部存储使用了一个Node数组(默认大小是16),而Node类包含一个类型为Node的next的变量,也就是相当
于一个链表,所有hash值相同(即产生了冲突)的key会存储到同一个链表里,
HashMap在并发执行put操作时会引起死循环,导致CPU利用率接近100%。因为多线程会导致HashMap的Node链表形成
环形数据结构,一旦形成环形数据结构,Node的next节点永远不为空,就会在获取Node时产生死循环。
一通过Collections.synchronizedMap()返回一个新的Map,这个新的map就是线程安全的,二重新改写了HashMap。三使用hashTable
HashTable源码中是使用synchronized来保证线程安全的
1)Lock并发包下的一个工具类是一个接口,能完成synchronize锁的全部功能,而synchronized是Java中的关键字,synchronized是内置的语言实现,synchronize主要用在线程较少、线程资源竞争不激烈的情况下,而lock锁则用在线程较多、线程资源竞争激烈的情况,synchronize的互斥锁在使用完CPU资源时会自动释放锁,lock(getlock()得到锁、unlock()释放锁)锁必须手动调用unlock()去释放锁unlock()必须在finally块中执行,否则有可能会造成死锁。
2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁。
3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断。
4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
5)Lock可以提高多个线程进行读操作的效率。
hashMap默认的负载因子大小为0.75,也就是说,当一个map填满了75%的bucket时候,和其它集合
类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,
并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的
bucket位置。
LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。
TreeMap实现SortMap接口,能够把它保存的记录 根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。
TreeMap取出来的是排序后的键值对。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。
LinkedHashMap 是HashMap的一个子类,如果需要输出的顺序和输入的相同,那么用LinkedHashMap可以实现,它还可以按读取顺序来排列,像连接池中可以应用。
1. 底层存储的数据结构不同
HashSet底层用的是HashMap哈希表结构存储,而TreeSet底层用的是TreeMap树结构存储
2.存储时保证数据唯一性依据不同
HashSet是通过复写hashCode()方法和equals()方法来保证的,而HashSet通过Compareable(ken pai er A bo)接口的compareTo()方
法来保证的
3.有序性不一样
HashSet无序,TreeSet有序
存储原理:
HashSet:底层数据结构是哈希表,本质就是对哈希值的存储,通过判断元素的hashCode方法和equals方法来保证
元素的唯一性,当hashCode值不相同,就直接存储了,不用在判断equals了,当hashCode值相同时,会在判断一
次euqals方法的返回值是否为true,如果为true则视为用一个元素,不用存储,如果为false,这些相同哈希值不
同内容的元素都存放一个桶里(当哈希表中有一个桶结构,每一个桶都有一个哈希值)
TreeSet:底层的数据结构是二叉树,可以对Set集合中的元素进行排序,这种结构,可以提高排序性能, 根据比较
方法的返回值确定的,只要返回的是0.就代表元素重复
因为ArrayList的底层是由一个Object[]数组构成的,而这个Object[]数组,默认的长度是10
超过了默认长度以1.5倍扩容
你所指的size()方法,指的是“逻辑”长度。
所谓“逻辑”长度,是指内存已存在的“实际元素的长度” 而“空元素不被计算”
不会,只会清除里面的非空数据,不会把空间清除掉
1.6没有最大容量限制的,但是jdk1.7做了一个改进,进行了容量限制。
1.6在容量进行扩展的时候,按照整除运算将容量扩展为原来的1.5倍加1,而jdk1.7是利用位运算,从效率上,jdk1.7就要快于jdk1.6。
1.6 1.7在初始化得时候他的长度都是设置好的是10,1.8在初始化的时候长度是null,在进行add时候他的长度才会变成10
ArrayList和LinkedList都是List接口的实现类 ,拥有List接口的特性。
不同点在于内存使用的0方式,因为内存使用方式的不同从而导致操作效率的不同。
ArrayList使用的是数组式的存储 ,内存开辟的空间连续,当集合中添加或删除一个元素时,这个元素之后的元素都需要移动。导致添加和删除的操作效率较低。单是因为空间连续读取的效率较高。
LinkedList使用的是链式存储 空间不连续,通过地址的指向连接数组中的每一个元素。这样的内存使用可以让那个元素的添加和删除的操作效率提高。单是读取的效率较低。
ArrayList和LinkedList都是线程不安全的,如果在多线程环境下建议使用Vector,Vector也是List接口的子类线程安全。
jdk1.7 新特性:
Duboo是一个分布式框架,zookeeper是duboo生产者暴露服务的注册中心。起一个调度和协调功能,当然注册中心也可以用redis 或者duboo自带的Multicast(mo ti ka si te) 来注册。
Duboo 通信方式采用长链接方式,所以当spring启动后链接就接通,duboo的消费者和生产者就可以直接调用。性能上高于其他http协议的请求。当时是为了解决单个服务器站点的压力,将项目拆分成页面加controller属于消费者,service+dao属于生产者,所有生产者暴露的端口都注册在zookeeper里面。这时候,消费者要调用生产者去zookeeper中取就可以了。所以我们部署了多套生产者,所有的消费者的请求可以由多个生产者去提供,具体由哪个生产者提供可以由zookeeper的配置去决定。如果某个生产者挂掉,zookeeper会加压力导向其他生产者,当这个生产者恢复状态的时候。Zookeeper会重新启用它。因为我们用n的执行,如果想让单个tomcat执行的action—service—dao 请求又多个tomcat来执行就可以使用 zookeeper+duboo 这时候一般是一个tomcat里面部署的是jsp+action所有的service接口都注册到了zookeeper里面,action去zookeeper里面通过duboo去调用哪个 service+dao 的组合。而且service+dao的组合可以配置多套。一套挂了其他的service+dao组合可以继续使用
dubbo框架的体系结构有5个核心组成部分,分别是提供者provider(普发一的),它的作用是为消费者提供数据。注册中心registry(ruai几丝追),它的作用是用来注册和发现服务。消费者consumer(肯酥么儿),它的作用是调用远程提供者提供的服务。监控中心Monitor(嘛呢de er)用来统计服务的调用次数以及调用时间,还有container(肯提呢er)用来充当容器来加载,运行服务提供者。
新建dubbo-provider(普发一的).xml配置文件,通过dubbo:application配置提供者应用名,通过dubbo:registry(ruai几丝追)配置注册中心的地址,通过dubbo:protocol(pro to ka)配置协议,以及通过dubbo:service来暴露要发布的接口。
最后我们在需要使用dubbo接口的项目中配置消费者信息,新建dubbo-consumer(肯酥么儿).xml文件,通过dubbo:application配置消费者应用名,通过dubbo:registry(ruai几丝追)指明要订阅的注册中心地址,通过dubbo:reference(ruai fu 让 ci)指定要订阅的服务接口。
配置dubbo集群来提高健壮性以及可用性。dubbo默认的集群容错机制是Failover(飞漏发)即失败自动切换,默认的重试次数为2,可以通过retries(为 chua aisi)调整。dubbo默认的负载均衡策略是Random随机,可以按权重设置随机概率。我们在写完dubbo提供者之后,为了测试接口的正确性,我们会进行直连测试。首先会在提供者端,通过将dubbo:registry(ruai几丝追)的register(ruai ji si te )设置为false,使其只订阅服务而不注册现在正在开发的服务;在消费者端,通过设置dubbo:reference(ruai fu 让 ci)的url,直连提供者进行测试。
被动说:所谓dubbo集群就是将dubbo的提供者部署多份,在不同的机器上或者说在同一台机器上用不同的端口号。从而在启动时可以向注册中心进行注册,这样结合dubbo的集群容错策略以及负载均衡策略可以提高可用性。
dubbo负载均衡策略:随机,轮询,最少活跃调用数。
dubbo的集群容错:失败自动切换,快速失败,失败安全。
dubbo+zookeerper实现session共享(在消费端):
我们的消费者只有一个模块,所有的请求首先都是进入这个模块里面, session都在消费者这个action里面,所有的请求都是首先进入这个项目里面,我们的生产者有多个模块。如果有多个消费者的情况下,会存在session共享问题,我们可以将session的id作为key值,用户对象作为value值存储到redis里面。当每次发送的请求的时候,拿着浏览器的session的id去redis里面取,如果能取到,证明用户已经存在,如果不能取到,就重新登录。
作用
数据发布与订阅(配置中心)
负载均衡
命名服务
分布式通知/协调
集群管理与Master(ma si de er)选举
分布式锁
分布式队列
Zookeeper可以被Dubbo 自带的multicast或者redis来替代
multicast,广播受到网络结构的影响,一般本地不想搭注册中心的话使用这种调用
redis通常和Zookeeper相辅相成来使用
两套东西,相辅相成,一般配合使用,举个例子,说明zookeeper如何配合redis使用。
在redis实际应用中,一般会有一个主节点redis,几个从属节点redis,主节点处理用户写请求,从节点向主节点同步数据,处理用户读的请求。当主节点故障的时候,从节点会通过raft算法选举出新的主节点处理用户写请求。
问题产生:如何在主服务器变更后,更新项目redis读写配置?
原始做法: 修改项目代码里面的redis地址和端口,然后上传代码,再重新部署
Zookeeper解决方法:通过zookeeper监控主从服务器变动,在项目里面引入zookeeper客户端,当主服务器变更的时候,zookeeper客户端会收到通知,在通知回调里,我们可以收到新的主节点地址和端口,根据他写一段redis重启代码,这样就避免了服务器重启
一.Failover Cluster 模式
失败自动切换,当出现失败,重试其它服务器。(缺省) 通常用于读操作,但重试会带来更长延迟。 可通过retries=”2”来设置重试次数(不含第一次)。
二.Failfast Cluster
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
三.Failsafe Cluster
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
四.Failback Cluster
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
五.Forking(fo gen) Cluster
1.并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。 可通过forks=”2”来设置最大并行数。
六.Broadcast(bu ruo ka si te) Cluster
广播调用所有提供者,逐个调用,任意一台报错则报错。(2.1.0开始支持)通常用于通知所有提供者更新缓存或日志等本地资源信息
开发时的三个优化:
1、开发者在本地开发的时候启动Dubbo比较麻烦,所以采用直接连接的配置;
2、开发者本地开发时会打断点调试,会超过Dubbo默认的超时时间1s,所以需要全局设置超时时间;
3、开发者本地时可能会先启动消费方服务,再启动提供方服务,为了先后启动没有顺序问题,所以需要设置不检查注册中心及提供方服务;
一、直接连接,即可以停止zookeeper服务;
(1)提供方的配置:
(2)消费方配置:
二、消费方设置超时时间
在服务消费方设置超时时间
三、消费方不检查注册中心及提供方的服务
将 check 参数设置为 “false”,如下
1、启动时检查
缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止Spring初始化完成,以便上线时,能及早发现问题,默认check=true。
关闭所有服务的启动时检查:(没有提供者时报错)
关闭某个服务的启动时检查:(没有提供者时报错)
其它的启动时检查还包括:注册中心
2、直连提供者
在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者,这时候可能需要点对点直连,
点对点直联方式,将以服务接口为单位,忽略注册中心的提供者列表。
3、服务分组
当一个接口有多种实现时,可以用group区分。
provider:
cosumer:
4、多版本
当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。
一般处理步骤
1)在低压力时间段,先升级一半提供者为新版本
2)再将所有消费者升级为新版本
3)然后将剩下的一半提供者升级为新版本
5、异步调用
可完成并行调用多个远程服务。异步总是不等待返回。
6、延迟暴露
如果你的服务需要Warmup时间,比如初始化缓存,等待相关资源就位等,可以使用delay进行延迟暴露。
当然,也可以配置到服务级别,但有些需要地方需要注意。
7、dubbo:protocol属性
threadpool:线程池类型,可选:fixed/cached ,默认fixed 。
threads :服务线程池大小(固定大小) ,默认为100
payload:请求及响应数据包大小限制,单位:字节,默认为88388608(=8M)
如:
ThreadPool
fixed 固定大小线程池,启动时建立线程,不关闭,一直持有。(缺省)
cached 缓存线程池,空闲一分钟自动删除,需要时重建。
limited可伸缩线程池,但池中的线程数只会增长不会收缩。(为避免收缩时突然来了大流量引起的性能问题)。
8、dubbo:application
name必填。当前应用名称,用于注册中心计算应用间依赖关系,注意:消费者和提供者应用名不要一样
.024、说说solr?
上个项目中我在做房源搜索的时候用到了solr,solr是一个企业级的搜索技术,底层是基于lucence的,solr相比于普通的搜索的好处关键在于它的分词器,但是在国内solr对中文支持不是很好,所以我们一般需要借助第三方的分词器,当初在网上查阅资料后,我们选取了一个IK分词器,在创建solr索引的时候我们配置了一个dataimport,让solr直接和数据库连接起来,solr搜索的时候不会去数据库搜索而是在solr的索引库里边搜索,所以我们在里边配置上需要创建索引数据的查询sql,需要注意的是我们在创建索引的时候用了哪个分词器,在搜索的时候也得用那个分词器。
···
增量索引:我们在需要创建索引的表中会加一个时间字段,通过配置的deltaQuery根具时间查出上一次创建索引之后的数据,然后solr就会把这些查出来的数据创建成索引
删除索引:删除索引的时候需要我们查到需要删除的数据的id,我们在需要创建索引的数据表中加入了一个逻辑删除字段,当我们需要删除索引的时候我们要先把数据库的逻辑字段状态给更改掉,然后通过配置的deletedPkQuery查找到逻辑删除的数据的主键id,然后solr就会帮我们把solr索引库中的索引给删除掉
更新索引:更新索引其实就是走的增量索引,更新数据库数据的同时将那个时间字段也给更新了,然后调用solr接口通知solr增量更新索引。
solr与项目的整合使用:
1.首先我们打开solr的服务器,在solr的server文件夹下面创建一个文件夹(文件夹名一般与业务相关),然后我们在这个文件夹中添加conf和data文件夹.
2.然后我们导入ik分词的jar包,并在之前创建的conf文件夹中的managered-schema配置有关ik分词器的标签。
3.然后我们获取mysql连接包,在conf文件夹中配置data-config.xml文件,文件当中配置数据库的链接地址、query、deltaQuery增量索引、deltaImportQuery、deletedPkQuery删除索引这些。
4.我们在managered-schema中添加查询出的字段对应的分词器。并在solrconfig.xml中配置加载data-config.xml文件和引入jar包(具体配置)
这样solr索引库与数据库链接创建好了。
接着就是与项目的真正结合。
整合:
1.首先导入solrj pom依赖:
HttpClient对认证机制+、提供了全面的支持。用户认证过程需要一组用户名/密码进行认证用户身份。HttpClient附带多个AuthScheme(a fu si gei mu)的实现。 AuthScheme接口表示一个抽象的面向挑战-应答的认证机制。解析和处理目标服务器发送的挑战并且对受保护资源的请求做出应答。
WebService一般通过iis进行匿名调用。这种方式不安全。我们可以使用CXF的拦截器通过自定义的Interceptor,可以改变请求和响应的一些消息处理,其中最基本的原理还是一个动态代理。设置拦截器。来验证用户的账号和密码。
WebService是完全基于XML的,HttpClient是基于HTTP协议的;WebService传输的是对象,HttpClient传输的是JSON格式的数据;
webservice和项目怎么整合?
首先1.我们当时发布webservice接口用的是cxf技术,当时用的是maven项目,然后呢在web.xml里配置cxfServlet,里面有一个url-parent(pan ruan te)指明cxf的请求地址,我们在pom.xml中导入cxf的相关jar包,然后我们在spring-cxf.xml,在这个文件里面首先通过import(in po te)引入cxf的配置文件,有三个(那三个)然后在
启动项目,访问发布的地址,在浏览器输入端口号+cxfServlet里面配置的url-parent(pan ruan te)的地址,spring-cxf.xml里面写接口的地址?wsdl回车,看一下wsdl文档是否完整,如果浏览器响应出来刚才指明方法的名字则webservice接口发布成功。
如何保证cxf接口的安全性?
首先webservice还是使用cxf,cxf里面自身支持一个拦截器,我们发布的接口里面加一个自定义的Interceptor,在里面配置一个PasswordCallback,密码认证,然后在密码认证的类里写明实现一个接口叫CallbackHandler ,在里面指明需要认证的用户名和密码,也就是说从里面把用户名和密码拿过来放到cxf里面,然后cxf就会帮我们自动去完成用户名和密码认证,如果认证通过则会调到接口,如果认证不通过则会抛出异常。
httpclient如何保证接口安全?
1、它自身没有CXF机制,所以得做安全认证,首先有的HTTPclient入口加上一个统一的签名认证,也就是说签名认证通过之后,才能调用详细的业务参数处理。怎么签名认证呢?根据一定的算法,根据传过来的参数,以及参数排序,排完之后进行MD5加密,加密之后把签名拿过来,我们把请求方请求的签名同样做一遍和我们签名认证相同的加密和签名认证作比较,如果它的加密之后和我加密之后的一样,证明签名认证通过,否则签名认证不通过。
2、保证我们接口与接口之间在内网相互调用
3、:尽管在同一个局域网内,但是必须通过VPN进行认证,两台服务器通过专用通道VPN,别人访问不到保证httpclient接口的安全。
当初我们做短信发送的时候用到了rabbitmq消息队列,我们当时有一个短信发送平台,平台采用监听消息队列方式进行短信的发送,我们在项目中通过配置一个RebbitConfig在上面加@Configuration注解,然后在里边new一个Queue对象创建一个消息队列,然后我们再发送短信的地方注入AmqpTemplate,最后通过调用AmqpTemplate.convertAndSend将消息发送到队列当中,convertAndSend里边有两个参数,第一个是消息队列名称,第二个是消息队列发送进去的内容。短信发送平台通过RabbitListener queues等于队列名称,监听队列,通过RabbitHandler注解获取到消息队列中的数据,然后通过httpClient调用短信发送接口,将短信发送出去并将发送的数据以及发送状态记录到数据库中。
。
a、普通索引
create table in1(
nid int not null auto_increment primary key,
name varchar(32) not null,
email varchar(64) not null,
extra text,
index ix_name (name)
)
create index index_name on table_name(column_name)
drop index_name on table_name;
show index from table_name;
#注意:对于创建索引时如果是BLOB 和 TEXT 类型,必须指定length。
create index ix_extra on in1(extra(32));
如何正确使用索引
select * from tb1 where name like ‘%n’;
存储过程:
存储过程是一个SQL语句集合,类似函数,需要主动调用。
1创建存储过程
delimiter //
create procedure p1()
BEGIN
select * from t1;
END//
delimiter ;
call p1()
都说了类似函数,那必须得可以接收参数,且参数有三类:
in 仅用于传入参数用
out 仅用于返回值用
inout 既可以传入又可以当作返回值
delimiter \ # 结尾分号改为\
create procedure p1(
in i1 int,
in i2 int,
inout i3 int,
out r1 int
)
BEGIN
DECLARE temp1 int; # 创建申明局部变量
DECLARE temp2 int default 0;
set temp1 = 1;
set r1 = i1 + i2 + temp1 + temp2;
set i3 = i3 + 100;
end\
delimiter ;
DECLARE @t1 INT default 3;
DECLARE @t2 INT;
CALL p1 (1, 2 ,@t1, @t2);
SELECT @t1,@t2;
函数
函数,与存储过程不同的是有return值。
1自定义函数
delimiter \
create function f1(
i1 int,
i2 int)
returns int
BEGIN
declare num int;
set num = i1 + i2
;
return(num);
END \
delimiter ;
索引优缺点:
优点: 1.创建唯一索引,保证数据库表中每一行数据的唯一性
2.大大加快数据的检索速度,这也是创建索引的最主要原因
3.减少磁盘IO(像字典一样可以直接定位)
缺点: 1.创建索引和维护索引.要耗费时间,这种时间随着数据量的增加而增加
2.索引需要占用额外的物理空间
3.当对表中的数据进行增加、删除和修改的时候,索引也要动态维护,降低了数据的维护速度
存储过程 优缺点
优点: 1.简化了复杂的业务逻辑,根据需要可以重复使用
2.屏蔽了底层细节,不暴露表结构即可完成操作
3.降低网络通信量,多条语句可以封装成一个存储过程来执行
4.设置访问权限来提高安全性
5.提高执行效率,因为它是预编译的以及存储在数据库中
缺点:1.可移植性差,相同的存储过程并不能跨多个数据库操作
2.大量使用存储过程后,首先会造成服务器压力大,而且维护难度逐渐增加
函数 优缺点:
优点: 1.函数允许标准组件式编程,提高了SQL语句的重用性、共享性和可移植性。
2.函数可以被作为一种安全机制来利用。
3.函数能够实现较快的执行速度,能够减少网络流量。
缺点: 1.函数的编写比单句SQL语句复杂。
2.在编写函数时,需要创建这些数据库对象的权限。
多线程产生死锁需要四个条件,分别是互斥性,保持和请求,不可剥夺性还有要形成闭环,这四个条件缺一不可,
只要破坏了其中一个条件就可以破坏死锁,其中最简单的方法就是线程都是以同样的顺序加锁和释放锁
*多线程产生死锁的四个必要条件:
互斥条件:一个资源每次只能被一个进程使用。
保持和请求条件:一个进程因请求资源而阻塞时,对已获得资源保持不放。
不可剥夺条件:进程已获得资源,在未使用完成前,不能被剥夺。+
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
Iterator是ListIterator的父接口,Iterator是单列集合公共取出容器中元素的方式。对于List,Set都通用。而
ListIterator是List集合的特有取出元素方式。Iterator中具备的功能只有hashNext(),next(),remove();
ListIterator中具备着对被遍历的元素进行增删改查的方法,可以对元素进行逆向遍历。
进程——资源分配的最小单位,线程——程序执行的最小单位。
1).什么是多线程
1个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务
进程 ->车间,线程->车间工人
多线程技术可以提高程序的执行效率
比如同时开启3条线程分别下载3个文件
2).多线程的原理
同一时间,CPU只能处理1条线程,只有1条线程在工作(执行)
多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)
如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
思考:如果线程非常非常多,会发生什么情况?
CPU会在N多线程之间调度,CPU会累死,消耗大量的CPU资源
每条线程被调度执行的频次会降低(线程的执行效率降低)
3)线程池:
是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程。线程池中线程的数量通常完全取决于可用内存数量和应用程序的需求。然而,增加可用线程数量是可能的。线程池中的每个线程都有被分配一个任务,一旦任务已经完成了,线程回到池子中并等待下一次分配任务。
2)Java提供的四种线程池的好处在于:
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能
进程和线程的区别?
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,
一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。
线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个程序至少有一个进程,一个进
程至少有一个线程。
宋氏多线程
线程呢主要是有三种方式,一种是继承thread类,另一种是实现runable接口,还有一种是使用线程池,在我们开发过程中主要还是使用,实现runable接口配合线程池多一些,因为如果使用继承thread类的话,我们的线程类就无法在继承其他的父类了,但是呢使用runable接口的话还可实现其他的接口也能继承其他的父类,互不影响,还有就是线程池,线程池呢据我了解常用的有4个,分别是newCachedThreadPool、newFixedThreadPool、newScheduledThreadPool、newSingleThreadExecutor。newCachedThreadPool是一个缓存线程池,如果线程池的长度超过处理需要,可灵活回收空闲的线程,若没有可回收的线程,就新建一个线程,适用于:执行很多短期异步的小程序或者负载较轻的服务器;newFixedThreadPool是一个定长线程池,可以控制线程最大的并发数量,超过的线程会在队列中等待,适用于:执行长期的任务,性能好很多; newScheduledThreadPool是一个定时线程池,支持定时及周期性任务的执行,创建一个定时线程池,支持定时及周期性任务执行;newSingleThreadExecutor是一个单线程池,它只会用唯一的工作线程来执行任务,保证所有的任务按照指定的优先级执行,适用于:周期性执行任务的场景。线程呢有6种状态 ,分别是新建状态、可运行状态、就绪状态、运行状态、阻塞状态、死亡状态。新建状态就是新创建了一个线程对象,可运行状态就是新建的线程调用strat方法后变成可运行状态,当这个线程获取到cpu分配的资源后变成就绪状态,调用run方法后线程变成运行状态,阻塞状态就是线程因为某种原因放弃CPU使用权,暂时停止运行,当阻塞状态完成后线程就会变成可运行状态,继续往下执行,直到线程变为死亡状态。阻塞状态分为等待阻塞、同步阻塞、其他阻塞,等待阻塞就是运行的线程执行wait()方法,JVM会把该线程放入等待池中,同步阻塞就是运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。其他阻塞就是运行的线程执行sleep方法或join方法,或者发出了IO请求时,JVM会把该线程设为阻塞状态。当sleep状态超时、join等待线程终止或者超时、或者IO处理完毕时,线程重新转入可运行状态。死亡状态:线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
不必主动说:
1、wait和sleep的区别?
wait时会释放锁资源但sleep不会释放锁资源,wait通常和notify以及notifyAll结合使用,需要notify或
者notifyAll对其进行唤醒,sleep通常在指定的时间内自动唤醒。
2、解决线程安全问题的方案?
a、通过加synchronized同步锁
b、避免使用全局变量和静态变量
c、ThreadLocal类用来提供线程内部的局部变量
3、如何用多线程实现生产者和消费者?
主要关键点 wait和notify的结合使用。
(1).因为生产者跟消费者共享一块区域,这里我将这块区域定义为“仓库”。
(2).仓库是有容量上限的,当数量达到上限后,生产者不允许继续生产产品.当前线程进入等待状态,等待其他线程唤醒。
(3).当仓库没有产品时,消费者不允许继续消费,当前线程进入等待状态,等待其他线程唤醒。
生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
要解决该问题,就必须让生产者在缓冲区满时调用wait方法(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能通过notify方法被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时调用wait方法进入休眠,等到生产者往缓冲区添加数据之后,再通过notify方法唤醒消费者
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
4、死锁如果产生如何避免?
多线程产生死锁需要四个条件,分别是互斥性,保持和请求,不可剥夺性还有要形成闭环,这四个条件缺一不可,
只要破坏了其中一个条件就可以破坏死锁,其中最简单的方法就是线程都是以同样的顺序加锁和释放锁
*多线程产生死锁的四个必要条件:
互斥条件:一个资源每次只能被一个进程使用。
保持和请求条件:一个进程因请求资源而阻塞时,对已获得资源保持不放。
不可剥夺条件:进程已获得资源,在未使用完成前,不能被剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
有序的资源分配法,
以同样的顺序加锁和释放锁
5、死锁产生的原因?
是由于访问共享资源顺序不当造成的。简单的说,死锁就是指两个或两个以上的线程在执行过程中,因争
夺资源而造成的一种互相等待的现象,如果没有外力作用,他们都将无法继续执行下去。
如果一个servlet的实例并不存在,Web容器加载servlet类。 创建一个servlet类的实例。只有在第一次访问的时候调用init初始化servlet实例。Service调用doget()、dopost()提供服务,servlet容器关闭是调用destroy销毁来结束该servlet。
Doget和dopost 其实并没有什么本质的区别 当form框里面的method为get时,执行doGet方法,使用get提交就必须在服务器端用doGet()方法接收;当form框里面的method为post时,执行doPost方法,使用post提交就必须在服务器端用doPost()方法接收
Final是一个修饰符:
当final修饰一个变量的时候,变量变成一个常量,它不能被二次赋值,当final修饰的变量为静态变量(即由static修饰)时,必须在声明这个变量的时候给它赋值。当final修饰方法时,该方法不能被重写,当final修饰类时,该类不能被继承Final不能修饰抽象类,因为抽象类中会有需要子类实现的抽象方法,(抽 象类中可以有抽象方法,也可以有普通方法,当一个抽象类中没有抽象方法时,这个抽象类也就没有了它存在的必要)Final不能修饰接口,因为接口中有需要其实现类来实现的方法
Finally:
Finally只能与try/catch语句结合使用,finally语句块中的语句一定会执行, 并且会在return,continue,break关键字之前执行
finalize:
Finalize是一个方法,属于java.lang.Object类,finalize()方法是GC (garbage collector垃圾回收)运行机制的一部分,finalize()方法是在 GC清理它所从 属的对象时被调用的
33.1、五个作用域:
1、singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例
2、prototype:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例
3、request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP
请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效
4、session:对于每次HTTP Session,使用session定义的Bean都将产生一个新实例。同样只有
在Web应用中使用Spring时,该作用域才有效
5、globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。
典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效
其中比较常用的是singleton和 两种作用域。对于singleton作用域的Bean,每次请求该Bean都将获得相同的实例。容器负责跟踪Bean实例的状态,负责维护Bean实例的生命周期行为;如果一个Bean被设置成prototype作用域,程序每次请求该id的Bean,Spring都会新建一个Bean实例,然后返回给程序。在这种情况下,Spring容器仅仅使用new 关键字创建Bean实例,一旦创建成功,容器不在跟踪实例,也不会维护Bean实例的状态。
33.2、springBean生命周期
Spring Bean的生命周期简单易懂。在一个bean实例被初始化时,需要执行一系列的初始化操作以达到可用的状态。同样的,当一个bean不在被调用时需要进行相关的析构操作,并从bean容器中移除。
Spring bean factory 负责管理在spring容器中被创建的bean的生命周期。Bean的生命周期由两组回调(call back)方法组成。
初始化之后调用的回调方法。
销毁之前调用的回调方法。
Spring框架提供了以下四种方式来管理bean的生命周期事件:
nitializingBean和DisposableBean回调接口
针对特殊行为的其他Aware接口
Bean配置文件中的Custom init()方法和destroy()方法
@PostConstruct和@PreDestroy注解方式
spring的核心是bean的依赖注入,注入到spring中的bean通常是以单例存在的,使用spring之后大大降低了java对内存的开销,避免了过多的对象创建,此处bean的生命周期是从tomcat初始化spring容器的时候创建,截止到tomcat关闭的时候bean销毁
34.1、Springboot:
spring boot 致力于简洁快速创建一个基于Spring的项目,让开发者写更少的配置,程序能够更快的运行和启动。并且它是spring cloud(微服务)的基础。在resources文件下下有一个application.yml文件,在里面写程序的配置文件。运行 Application的main(),呈现会启动,
1.独立运行的Spring项目,Spring Boot可以以jar包的形式来运行,运行一个Spring Boot项目我们只需要通过java -jar xx.jar类运行。非常方便。
2.内嵌Servlet容器
Spring Boot可以内嵌Tomcat,这样我们无需以war包的形式部署项目。
3.提供starter简化Maven配置,使用Spring或者SpringMVC我们需要添加大量的依赖,而这些依赖很多都是固定的,这里Spring Boot 通过starter能够帮助我们简化Maven配置。4.自动配置Spring
5.准生产的应用监控
6.无代码生成和xml配置
Spring Boot的核心思想就是约定大于配置,一切自动完成。
Springboot定时器
配置呢也很简单,只需要在启动类加上@EnableScheduling注解 在需要定时的方法中加上@Scheduled(cron=””)注解就可以啦 cron就是配置按时间执行方法的地方就可以啦。
34.2、springboot监控:
springboot中呢有一个叫spring-boot-admin的jar包用来做springboot项目的监控,配置呢也很简单,只需要在监控的服务端项目中添加spring-boot-admin-server.jar和spring-boot-admin-server-ui.jar包就可以,然后在application.yml中配置spring.boot.admin.context-path 指明监控的服务地址,在main方法启动类上加上EnableAdminServer注解,这样服务端就配置好了,在需要被监控的项目中添加spring-boot-admin-starter-client.jar包,在application.yml配置文件中配置spring.boot.admin.url指明监控服务的地址,这样被监控端也就配置好了,然后我们访问监控服务在页面就可以看到被监控项目的内存使用情况,项目的健康情况,jvm的使用情况,网络路由情况,线程运行情况、还有数据源的一些信息等等。
34.3、说说你对springcloud的了解?
Spring Cloud是一个相对比较新的微服务框架,它集成了众多开源的框架,利用Spring Boot的开发便利性实现了服务治理、服务注册与发现、负载均衡、数据监控,REST API发布方式等,基本囊括了分布式框架所需要的所有功能。是一套易开放、易部署、易维护的分布式开发工具包,要使用springCloud框架呢我们首先需要创建一个服务注册中心Eureka,创建Eureka呢首先需要搭建一个springCloud微服务,然后再启动类上面加上一个注解@EnableEurekaServer,然后只需要再application.yml配置文件中配置Eureka的连接信息,这样Eureka就配置好了,通常情况下为了方便,我们一般将这个Eureka服务呢是打成war包,然后配置bat启动文件,每次只需要直接使用bat文件启动就可以了,这样注册中心就已经完成了。
然后呢我们需要配置一个服务提供者用来提供服务,服务提供者呢也是一个单独的微服务,需要在注册中心进行注册,也就是在application.yml里面配置注册中心Eureka的连接信息,然后在启动类上面加上@EnableEurekaClient注解,表明是一个eurekaclient。这样服务中心也就配置好了。
紧接着我们需要创建一个服务消费者用来调用服务提供者提供的方法,springCloud中有两种服务调用方式,一种是ribbon,另一种是feign。ribbon是一个负载均衡客户端,可以很好的控制htt和tcp的一些行为,而Feign默认集成了ribbon,所以我们一般使用的是Feign作为服务消费者,feign需要在它的pom文件引入Feign的起步依赖spring-cloud-starter-feign、Eureka的起步依赖spring-cloud-starter-eureka、Web的起步依赖spring-boot-starter-web,需要在启动类上面加上@EnableFeignClients这个注解,开启Feign的功能,然后在application.yml配置文件中配置注册中心Eureka的地址,这样服务消费者就配置好了,然后我们需要在service的接口中调用服务提供者提供的服务,只需要在接口上面加上@FeignClient(value = “要调用的服务名”)指明要调用的服务名称,然后再方法上面使用@RequestMapping注解,value值要和那个服务提供者里面的value值保持一致,并且如果方法有参数的话需要使用@RequestParam这个注解,这样服务消费者就可以调用服务提供者的服务了。
为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应,为了解决这个问题,springCloud提供了断路器,其实就是在服务之间调用的时候发生异常的一种解决方案,当异常产生的时候呢就不再调用服务提供者的方法了,而是执行我们配置好的回调方法,一般我们是用这个方法返回一也异常的提示信息。Feign是自带断路器的,在D版本的Spring Cloud中,它没有默认打开。需要在配置文件中配置打开它,在配置文件添加feign.hystrix.enabled=true打开,然后我们需要修改service接口上的@FeignClient(value = “要调用的服务名”)后面加上fallback = 回调函数的类名.class,然后创建一个回调函数的类,并且实现这个接口,重写当前方法,方法内容一般为返回的提示信息,这样断路器也就配置好了。
路由网关Zuul的主要功能是路由转发和过滤器,我们需要创建一个新的微服务,在pom文件里面添加spring-cloud-starter-zuul,之后再启动类上面加上@EnableZuulProxy,开启zuul的功能,然后需要在application.yml配置文件中配置Eureka注册中心和Zull的配置信息。zuul不仅只是路由,并且还能过滤,做一些安全验证。
svn和git最大的几个区别要点,svn必须要有服务端,网络能连上服务端才能提交和更新,git不需要,每一台装了git的电脑都是服务端,各台电脑之间可以相互同步和推送,而提交不需要网络就可以提交到本地的git库里。
对于吧友们来说,这样的好处就是,如果要分享代码,不需要打个压缩包传来传去,也不需要找个服务器搭个svn来共享,现在oschina,csdn等网站都提供了免费的git服务器,大家注册个帐号,提交下去,把地址发给别人就可以了,而免费的svn服务器,几乎是不存在的。而且svn因为服务端是单一的,一旦服务器坏了,整个版本库的历史记录就没有了,也没法再回滚,git每台电脑都是服务端,只要两台电脑做过同步,任何一台坏了,另一台还保有着所有的历史记录,仍然可以提交更新回滚,不怕代码历史丢失。
相同:
能记录文件的所有更改记录。这样是为了大量更改后,但是最后觉得还是原来的版本代码好,可以有记录回到过去,而不用采用 Copy 旧代码另存为某文件,然后某个时间从大量文件中找你需要的历史记录,版本控制帮我们做到了历史记录的存储,可以方便地查询及回滚到过去的某一版本。
不同:
git和其他版本控制系统(如 CVS)有不少的差别,git本身关心文件的整体性是否有改变,但多数的 CV S或 Subversion 系统则在乎文件内容的差异。因此git更像一个文件系统,直接在本机上获取数据,不必连接到主机端获取数据。
git 是用于Linux内核开发的版本控制工具。与CVS、Subversion(SVN) 一类的集中式版本控制工具不同,它采用了分布式版本库的作法,不需要服务器端软件,就可以运作版本控制,使得源代码的发布和交流极其方便。git的速度很快,这对于诸如Linux内核这样的大项目来说自然很重要。git最为出色的是它的合并追踪(merge tracing)能力。
SVN 是集中式或者有中心式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以首先要从中央服务器哪里得到最新的版本,然后干活,干完后,需要把自己做完的活推送到中央服务器。集中式版本控制系统是必须联网才能工作,如果在局域网还可以,带宽够大,速度够快,如果在互联网下,如果网速慢的话,就纳闷了。
Git 是分布式版本控制系统,那么它就没有中央服务器的,每个人的电脑就是一个完整的版本库,这样,工作的时候就不需要联网了,因为版本都是在自己的电脑上。既然每个人的电脑都有一个完整的版本库,那多个人如何协作呢?比如说自己在电脑上改了文件A,其他人也在电脑上改了文件A,这时,你们两之间只需把各自的修改推送给对方,就可以互相看到对方的修改了
1.SVN优缺点
优点:
1、 管理方便,逻辑明确,符合一般人思维习惯。 2、 易于管理,集中式服务器更能保证安全性。 3、 代码一致性非常高。 4、 适合开发人数不多的项目开发。
缺点:
1、 服务器压力太大,数据库容量暴增。 2、 如果不能连接到服务器上,基本上不可以工作,看上面第二步,如果服务器不能连接上,就不能提交,还原,对比等等。 3、 不适合开源开发(开发人数非常非常多,但是Google app engine就是用svn的)。但是一般集中式管理的有非常明确的权限管理机制(例如分支访问限制),可以实现分层管理,从而很好的解决开发人数众多的问题。
2.Git优缺点
优点:
1、适合分布式开发,强调个体。 2、公共服务器压力和数据量都不会太大。 3、速度快、灵活。 4、任意两个开发者之间可以很容易的解决冲突。 5、离线工作。
缺点:
1、学习周期相对而言比较长。 2、不符合常规思维。 3、代码保密性差,一旦开发者把整个库克隆下来就可以完全公开所有代码和版本信息。
首先jvm 是java虚拟机,是完成java程序一次编译到处执行,跨平台关键所在。不同的平台使用不同的jvm。Java源文件是先通过编译器编译成字节码文件,然后交给jvm运行的。
jre是java运行时环境,运行java程序的必要因素,jre里包含jvm和一些其他的核心类库。
jdk是开发工具包里面包含了开发的工具,如java javac等命令。而且jdk中包含jre。所以如果安装了jdk就不需要安装jre。
三者的关系 是 jdk包含jre ,jre包含jvm。
面向对象编程有四个特征:抽象,封装,继承,多态。
多态有四种体现形式:
重写:重写发生在子类继承父类的关系中,父类中的方法被子类继承,方法名,返回值类型,参数完全一样,但是方法体不一样,那么说明父类中的该方法被子类重写了。
重写发生在继承关系中,在子类中重写父类的方法,要求方法名,参数列表,返回值完全一致,子类重写的方法的访问权限不能小于父类的范围,子类重写的方法不能抛出比父类更多更大的异常,子类不能重写父类的private方法和 static方法,在父类方法无法实现子类需求时 我们重写父类的方法
数据类型是为了区分存储的数据,不同的数据规定不同的存储空间,存储不同的数据类型
Java中的数据类型 分为 基本数据类型和引用数据类型
基本数据类型中又分为了 数字型,字符型,布尔型
数字型中分为了整数和小数
整数数据类型中包括
byte 字节型 存储空间1字节 默认值是 0 8位
short 短整形 存储空间 2字节 默认值是0 16位
int 整形 存储空间 4字节 默认值是0 32位
long 长整形 存储空间 8字节 默认值是0 64位
小数类型包括
float 单精度小数类型 存储空间是4字节 默认值是0.0f 32位
double 双精度小数类型 存储空间是8字节 默认值是0.0d 64位
字符型 是char 存储空间是2字节 只能存储一个中文或英文字符。一个中文字符占两个字节,一个英文字符占一个字节。字符型的常量需要 ‘ ’ 单引号包裹 16位
布尔类型 boolean 存储空间1字节 这个类型只能存储真true 和假false
引用数据类型中 包括 数组array 类class 接口interface
java支持的数据类型有哪些?什么是自动拆装箱?什么时候自动装箱不起作用?
2.1java中的8种基本数据类型:boolean byte char short int float double long
2.2:基本数据类型和它对应的封装类型之间可以相互转换,从基本数据类型到封装类
型叫做装箱,从封装类型到基本数据类型叫拆箱,自动拆装箱是jdk5.0提供的新特特性
2.3当我们要调用的方法中存在重载的时候,即基本类型数据作为唯一参数的方法与
该基本类型包装类作为唯一参数的方法重载,这时候自动装箱不起作用。
输入/输出对象: request response out
作用域通信对象: session application pageContext
Servlet 对象: page config
错误对象: exception (E ke sai pu shen)
四个作用域从大到小:appliaction>session>request>page
List和Set都是接口,他们都继承于接口Collection(ke lai ke shen),List是一个有序的可重复的集合,而Set的无序的不可重复的集合。Collection是集合的顶层接口,Collections(ke lai ke shen si)是一个封装了众多关于集合操作的静态方法的工具类,因为构造方法是私有的,所以不能实例化。
2.List接口实现类有ArrayList,LinkedList,Vector(wai de er)。ArrayList和Vector是基于数组实现的,所以查询的时候速度快,而在进行增加和删除的时候速度较慢LinkedList是基于链式存储结构,所以在进行查询的时候速度较慢但在进行增加和删除的时候速度较快。又因为Vector是线程安全的,所以他和ArrayList相比而言,查询效率要低。
1、两两比较 向后交换
for(int i=0;i
int temp = arrayInt[j];
arrayInt[j]=arrayInt[j+1];
arrayInt[j+1] = arrayInt[j];
}
}
}
王梦璇版:
For(int a=0; a
int temp =arr[b+1];
arr[b+1] =arr[b];
arr[b] =temp;
}
}
}
//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton {
private Singleton() {}
private static Singleton single=null;
//静态工厂方法
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1 {
private Singleton1() {}
private static final Singleton1 single = new Singleton1();
//静态工厂方法
public static Singleton1 getInstance() {
return single;
}
}
命令:
1.ifconfig:查看ip地址
2.java -version:查看jdk的版本
3.rpm -qa | grep 软件的名称:查找和指定名称相关的软件
4.rpm -e --nodeps 软件名称:卸载指定的软件
5.rpm -ivh 软件名称: 安装指定的软件
6.uname -a :查看linux系统的基本信息(计算机名,操作的位数,版本号)
7.ll :用来查看当前目录下的所有文件资源。
8.mkdir 目录名:创建文件夹
9. vi 文件名:对指定的文件名进行编辑。
:wq! 强制保存并退出,:q! 强制退出
10.pwd : 查看当前目录的完整路径
11.unzip 文件名.zip :解压后缀名为zip的压缩文件
12.mv 源文件名 目标文件名:重命名的作用
13.rm -rf 文件夹名 : 递归强制删除文件夹及其下面的所有子文件
14.service iptables stop:禁用防火墙 service iptables start:开启防火墙
15.chmod +x .sh:使所有后缀名为sh的文件,拥有可执行权限
16. 在bin目录下./startup.sh启动tomcat
17. 在bin目录下通过tail -f …/logs/catalina.out 来查看启动日志
18. ps -ef | grep 进程名 :查看指定进程是否启动。
19. kill -9 进程号:强制杀死进程
20. touch 文件名称: 创建文件
21. cat 文件名称: 查看文件内容
22.
cd 文件夹路径 :跳转到指定的文件夹目录
cd / :跳转根目录
cd …/ :跳转到上级目录
cd …/…/ :跳转到上两级目录
23. reboot :重启机器
24. cp 源文件路径 新文件目录 :复制文件
25. ping 测试通讯链接
26. clear 清屏
27. tar -cvf 文件名.tar 要压缩的文件 :将指定的文件打包成tar
28. tar -xvf 文件名.tar :解压后缀名为tar的文件
29. ctrl + c :退出进程 多用于 退出查看日志等
30.
开放端口:/sbin/iptables -I INPUT -p tcp --dport 8080 -j ACCEPT
保存设置:/etc/rc.d/init.d/iptables save
重启防火墙/etc/init.d/iptables restart
31.tar -zcvf 文件名.tar.gz 要压缩的文件 :将指定的文件打包压缩成tar.gz
32.tar -zxvf 文件名.tar.gz :解压缩后缀名为tar.gz文件
rpm是linux下一种软件安装包的后缀名,如.rpm,等同于windows中的exe.
rpm是一个命令,用来进行和软件安装相关的操作。(安装,卸载,查找)
linux下没有盘符的概念,它是通过相关的目录来管理资源的。
我们通常在/home创建文件夹来存放需要安装的软件。
tab键自动补全
JDK默认安装在/usr/java
设置jdk的环境变量:
修改/etc/profile文件
·用文本编辑器打开/etc/profile
·在profile文件末尾加入:
export JAVA_HOME=/usr/java/jdk1.7.0_71
export PATH= J A V A H O M E / b i n : JAVA_HOME/bin: JAVAHOME/bin:PATH
export CLASSPATH=.: J A V A H O M E / l i b / d t . j a r : JAVA_HOME/lib/dt.jar: JAVAHOME/lib/dt.jar:JAVA_HOME/lib/tools.jar
针对tomcat的安装,则直接解压缩后便可使用。
select * from (select t.*, rownum rn from (select * from menu order by id desc) t where rownum < 10) where rn >=5
session是存储在服务器端,cookie是存储在客户端的,所以安全来讲session的安全性要比cookie高,然后我们获取session里的信息是通过存放在会话cookie里的sessionid获取的。又由于session是存放在服务器的内存中,所以session里的东西不断增加会造成服务器的负担,所以会把很重要的信息存储在session中,而把一些次要东西存储在客户端的cookie里,然后cookie确切的说分为两大类分为会话cookie和持久化cookie,会话cookie确切的说是存放在客户端浏览器的内存中,所以说他的生命周期和浏览器是一致的,浏览器关了会话cookie也就消失了,然而持久化cookie是存放在客户端硬盘中,而持久化cookie的生命周期就是我们在设置cookie时候设置的那个保存时间,然后我们考虑一问题当浏览器关闭时session会不会丢失,从上面叙述分析session的信息是通过sessionid获取的,而sessionid是存放在会话cookie当中的,当浏览器关闭的时候会话cookie消失所以我们的sessionid也就消失了,但是session的信息还存在服务器端,这时我们只是查不到所谓的session但它并不是不存在。那么,session在什么情况下丢失,就是在 服务器关闭的时候,或者是sessio过期,再或者调用了invalidate()的或者是我们想要session中的某一条数据消失调用session.removeAttribute()方法,然后session在什么时候被创建呢,确切的说是通过调用session.getsession来创建,这就是session与cookie的区别
GC的全称是garbage collection,中文名称垃圾回收,是.net中对内存管理的一种功能。垃圾回收器跟踪并回收托管内存中分配的对象,定期执行垃圾回收以回收分配给没有有效引用的对象的内存。当使用可用内存不能满足内存请求时,GC会自动进行。 在进行垃圾回收时,垃圾回收器回首先搜索内存中的托管对象,然后从托管代码中搜索被引用的对象并标记为有效,接着释放没有被标记为有效的对象并收回内存,最后整理内存将有效对象挪动到一起。这就是GC的四个步骤。 由上可见,GC是很影响性能的,所以一般说来这种事情况还是尽量少发生为好。 为了减少一些性能影响,.net的GC支持对象老化,或者说分代的概念,代是对象在内存中相对存现时期的度量单位,对象的代数或存现时期说明对象所属的代。目前.net的垃圾回收器支持三代。每进行一次GC,没有被回收的对象就自动提升一代。较近创建的对象属于较新的代,比在应用程序生命周期中较早创建的对象的代数低。最近代中的对象位于零代中。每一次GC的时候,都首先回收零代中的对象,只有在较低代数的对象回收完成后仍不能满足需求的情况下才回收较高代数的对象。
Java中垃圾回收有什么目的?什么时候进行垃圾回收?
目的:回收堆内存中不再使用的对象,释放资源
什么时候回收:可以手动调用gc,一般是系统等到新生代的内存区占满了又需要分配内存的时候,这个时候新生代就变成了老年代,等老年代的内存占满之后开始回收老年代所占的内存区。
多线程这块我在工作中也有涉及到,我对于线程安全不安全是这么理解的,我认线程的安全与否与java类本身无关,也就是说不能说StringBuffer是线程安全的,stringBuilder是线程不安全的,或hashMap是线程不安全的,HashTable是线程安全的,这几个类本身是与线程无关的,只有当它处于多线程的环境当中时,才可能引发线程安全不安全的问题,StringBuffer与stringBuilder的线程安全与否取决于他的append方法前面有没有开启互斥锁,对于HashMap与HashTable的线程与否,主要取决于put方法前面有没有开启线程的互斥锁,(线程本身就自带互斥锁,只不过默认不开启,当看到synchronized时会开启互斥)。
通过我的工作经验,我感觉只要不让多个线程同时操作同一个对象,一般不会涉及到线程不安全这种问题,只有到多个线程同时操作同一个对象的时候才有可能引发线程安全问题
1.get是从服务器上获取数据,post是向服务器传送数据,
2.get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。
3.get安全性非常低,post安全性较高。但是执行效率却比Post方法好。
4.在进行文件上传时只能使用post而不能是get。
5.get传输的数据在地址栏可见,post的数据不可见。
1.StringBuilder执行效率高于StringBuffer高于String.
2.String是一个常量,是不可变的,所以对于每一次+=赋值都会创建一个新的对象, StringBuffer和StringBuilder都是可变的,当进行字符串拼接时采用append方法,在原来的基础上进行追加,所以性能比String要高,又因为StringBuffer 是线程安全的而StringBuilder是线程非安全的,所以StringBuilder的效率高于StringBuffer.
3.对于大数据量的字符串的拼接,采用StringBuffer,StringBuilder.
Jvm优化、tomcat优化、sql优化、数据库优化、代码优化
代码优化:
1、及时关闭流
Java编程过程中,进行数据库连接、I/O流操作时务必小心,在使用完毕后,及时关闭以释放资源。因为对这些大对象的操作会造成系统大的开销,稍有不慎,将会导致严重的后果。
2、尽量减少对变量的重复计算
明确一个概念,对方法的调用,即使方法中只有一句语句,也是有消耗的,包括创建栈帧、调用方法时保护现场、调用方法完毕时恢复现场等。所以例如下面的操作:
for (int i = 0; i < list.size(); i++)
{…}
建议替换为:
for (int i = 0, int length = list.size(); i < length; i++)
{…}
这样,在list.size()很大的时候,就减少了很多的消耗
3、for循环里减少对数据库的交互
4、不要在循环中使用try…catch…,应该把其放在最外层
5、尽量使用HashMap、ArrayList、StringBuilder,除非线程安全需要,否则不推荐使用Hashtable、Vector、StringBuffer,后三者由于使用同步机制而导致了性能开销
6、将重复的代码封装起来,尽量避免重复代码的出现
服务端:
需要导入cxf-rt-transports-http-jetty.jar包,写一个接口和实现类, 在接口的类名上需加@WebService注解,接口的方法上需加@WebMethod注解, 之后写一个发布类,使用EndPoint.publish()方法发布服务,访问发布的地 址,查看生成的wsdl文档是否完整(完整的wsdl文档包含types messages portType binding service五部分);
客户端:
首先根据服务端发布的地址使用cxf框架工具或jdk(1.6版本以上)生成 中间桥梁类,然后写一个中间桥梁类的服务类,通过调用中间桥梁类中的方 法来访问服务端数据,从而达到了远程调用的目的Spring与cxf结合
webservice服务端配置流程
首先在web.xml中引入cxfServlet核心类,指定对以/cxf开头的url路径 提供webservice服务,之后我们在要发布成webservice接口上添加 @Webservice 注解,而且还要在实现类上添加同样的webservice注解并且 要说明实现了哪个接口,之后在spring-webservice.xml中发布webservice 服务,通过jaxws:endpoint这个标签,并且在标签配置implementor和 address来表明实现服务的类,以及发布的地址,最后在浏览器中输入相关 的webservice地址?wsdl来验证服务是否发布成功。
webservice客户端的配置
首先通过wsdl2java根据发布的webservice服务端地址的wsdl生成客户端 调用的中间桥梁java类,将生成的java类拷贝到客户端项目中,配置 spring-client.xml文件,通过jaxws:client定义一个bean,并通过address 属性指明要访问的webservice的服务地址,通过serviceClass指明充当中 间桥梁的服务类,之后获取该bean,就可以通过它来访问发布的webservice 接口中的方法。
springMvc是基于方法的通过方法里的参数来接受前台传过来的值,默认使用的是单例线程不安全,它是通过servlet实现的请求转发和初始化,开发效率高于Struts2.dispatherServlet是前端控制器,springMvc中的Interceptor是通过HandlerInterceptor来实现的。
Struts2是基于类的通过声明全局的私有属性并生成get、set方法来接收前台传过来的值,默认是使用多例线程安全,Struts2是通过filter实现的请求转发和初步处理。Struts2有自己的拦截机制,配置在Struts.xml中,拦截Struts的action请求并处理,struts2核心控制器是FilterDispatcher 旧版本,StrutsPrepareAndExecuteFilter 新版本。
redis的一大特点就是可以将数据进行持久化,在一定程度上确保了数据的安全性,但不是绝对的;
首先持久化分为rdb(快照持久化)和aof(精细持久化);
快照持久化,是默认开启的;会自动保存数据,当启动时会在文件夹中生成dump.rdb文件;存放持久化后的数据;
当然我们也可以设置持久化的频率,在redis.conf文件中通过save进行设置,默认有三种情况,每秒超过一万数据或每5分钟有10条数据的时候再或者每15分钟有1条记录,都会执行快照持久化,
当然也可以通过bgsave的方法来手动进行一个快照持久化;(也可以通过ip和端口号就给别人进行手动持久化);
如果频繁的快照持久化,会降低性能和效率,
但是这样也出现了一个问题,就是当一分钟内如果有一万条数据时,不会提交,但是在下一次提交之前,停电了,这样就会丢失掉这些数据;
当时想到的解决方法呢就是和(AOF)精细持久化进行一个结合,达到一个秒级的持久化;
这个持久化需要我们手动进行开启,(注意,AOF开启之后,之前的数据都会丢失,所以要在一开始使用时就要配置好)开启的方法就是在配置redis.conf,将appendonly 改为yes;同时还可以更改文件名称;然后重新启动服务,这时精细化持久化就启动好了
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。
mybatis是一个轻量级的持久层封装框架,对jdbc进行简单的封装,基于orm框架实现原理开发,相对于Hibernate框架来说,mybatis属于半自动持久层框架。
目前mybatis在整体的web开发项目过程中,占比在70%以上,目前的项目整体都是大型的项目,在大项目的开发过程中,Hibernate完全体现不出开发效率高的价值,大型项目一般数据库表关系复杂,Hibernate不支持复杂的联查,还是需要把hql语句转换成sql语句来执行,而且大型的项目对于持久层优化反面比较注重,hql不利于做sql优化。
mybatis的常用标签以及属性
查询返回的结果集映射关系配置
查询标签
新增标签
修改标签
删除标签
进行判断
id 相当于与实现类中的方法名,与接口中的方法名必须一致
resultMap 当前查询结果集对应的映射关系
parameterType 参数类型 前台页面或者Service层传递给dao层的参数类型,一般为实体类的类型
resultType 声明返回值类型 常用的有 int String map
1.支持sql语句的优化,是程序员对sql语句的把控能力更大
2.在整体的开发效率中与Hibernate对比,并没有相差太多
3.相对于sql语句有很好的重复利用度
4 .mybatis sql语句编写更加灵活,支持for循环 if esle 判断
5 .支持查询结果转换各种类型格式
注:区别:$符号取值相当于sql语句中的拼接字符串,#号取值相当于占位符取值
19.2、开启二级缓存
SSM:
1.导入MyBatis-EhCache整合包
2.classpath下添加EhCache配置文件(ehcache.xml)
3.MyBatis配置文件(SqlMapConfig.xml)打开二级缓存
4.Mapper配置文件添加cache标签
5.缓存结果继承序列化接口
public class User implements Serializable
springBoot:
1.在pom.xml中引入cache依赖,添加如下内容:
org.springframework.boot
spring-boot-starter-cache
2.在Spring Boot主类中增加@EnableCaching注解开启缓存功能
3.
相同点
· Hibernate与MyBatis都可以是通过SessionFactoryBuider由XML配置文件生成SessionFactory,然后由SessionFactory 生成Session,最后由Session来开启执行事务和SQL语句。其中SessionFactoryBuider,SessionFactory,Session的生命周期都是差不多的。
· Hibernate和MyBatis都支持JDBC和JTA事务处理。
差异点
全自动和半自动;
因为Hibernate对查询对象有着良好的管理机制,用户无需关心SQL。所以在使用二级缓存时如果出现脏数据,系统会报出错误并提示;
而MyBatis在这一方面,使用二级缓存时需要特别小心。如果不能完全确定数据更新操作的波及范围,避免Cache的盲目使用。否则,脏数据的出现会给系统的正常运行带来很大的隐患。
JUnit是一个开放源代码的Java测试框架,继承TestCase类,用于编写和运行可重复的测试。他是用于单元测试框架体系xUnit的一个实例(用于java语言)。它包括以下特性:
1、用于测试期望结果的断言(Assertion)
2、用于共享共同测试数据的测试工具
3、用于方便的组织和运行测试的测试套件
4、图形和文本的测试运行器
输入/输出对象: request response out
作用域通信对象: session application pageContext
Servlet 对象: page config
错误对象: exception
四个作用域从大到小:appliaction>session>request>page
1、从数据共享上
Forword是一个请求的延续,可以共享request的数据
Redirect开启一个新的请求,不可以共享request的数据
2、从地址栏
Forword转发地址栏不发生变化
Redirect转发地址栏发生变化
24、KaTeX parse error: Expected 'EOF', got '#' at position 4: {}和#̲{}在mybatis的里面的区…{}是Properties(pa pu tei si)文件中的变量占位符,它可以用于标签属性值和sql内部,属于静态文本替换
2).#{}是sql的参数占位符,Mybatis会将sql中的#{}替换为?号,在sql执行前会,按序给sql的?号占位符设置参数值
25、就业必会 复杂sql?
1):oracle 分页:
select * from (select t.*, rownum rn from (select * from menu order by id desc) t where rownum < 10) where rn >=5
2): mysql 分页:
select * from music where id limit 5,5
3):oracle中如何快速将一张表的数据复制到另外一张表中(另外一张表不存在,另外一张表存在,但数据为空)。
不存在另一张表时:(表的备份)
create table 新表 as select * from 将要复制的表
存在另一张表时:(批量插入)
insert into 新表名 select 字段 from 将要复制的表名
4)音乐专辑查询出special app:ds:special表中的id 专辑名 并下面有多少首歌曲:
Select s.id , min(s.sname),count(m.mid) from special s inner join ms m on s.id=m.id group by s.id
5):快速删除一张表(不可事物回滚,也就是没有日志记录)
TRUNCATE from 表名
6):inner join:
select 查找信息 from 表名 1 inner join 表名2 on 表名1.列名 = 表名2.列名
7):left join:
左外连接 select 查找信息 from 表名1 left join 表名2 on 表名1.列名 = 表名2.列名
8):right join:
右外连接 select 查找信息 from 表名1 right join 表名2 on 表名1.列名 = 表名2.列名
9):oracle中查询遍历树形结构(start with)
select * from extmenu start with pid=1 connect by prior id = pid
快速删除父节点以及父节点下的所有节点:
Delete from extmenu where id in (elect * from extmenu start with pid=1 connect by prior id = pid)
10):查询出来60-70,80-90,95-100学生的信息:
select * from stu where chengji between 60 and 70 or between 80 and 90 or between 95 and 100
select * from stu where chengji > 60 and chengji < 70 or chengji > 80 and chengji < 90 or chengji > 95 and chengji < 100
11):用exists替换in------进行联表查询:
select * from dept where exists(select * from emp where emp.deptno=dept.deptno);
或 select * from dept d inner join emp e on d.deptno = e.deptno(只查询出两表共同拥有的字段数据)
12):删除表中的重复数据:
delete from xin a where a.rowid != (select max(b.rowid) from xin b where a.name = b.name);
13):row_number(),rank() over ,dense_rank() over 按工资排序:
select sal, row_number() over(order by sal desc) rank1,rank() over(order by sal desc) rank,dense_rank() over(order by sal desc) drank from emp
14):select * from (select emp.* from( dense_rank() over(partition by departNo order by sal desc) rk from emp ) Where rk=4
26、Redis 和Mongodb 的 区别?
1、启动tomcat时加载web.xml文件,核心控制器FilterDispatcher会加载并且解析struts.xml文件
2、从页面发送一个请求,核心控制器FilterDispatcher会根据后缀名进行拦截
3、FilterDispatcher会根据配置信息把请求发送给具体的Action类中的指定方法
4、执行相关的业务逻辑,并且返回一个String字符串
5、配置文件会根据result中name的属性值与返回的字符串进行匹配,匹配成功则跳转到相对应的jsp页面
*
整个处理过程从一个HTTP请求开始:
1.Tomcat在启动时加载解析web.xml,找到spring mvc的前端总控制器DispatcherServlet,并且通过DispatcherServlet来加载相关的配置文件信息。
2.DispatcherServlet接收到客户端请求,找到对应HandlerMapping,根据映射规则,找到对应的处理器(Handler)。
3.调用相应处理器中的处理方法,处理该请求后,会返回一个ModelAndView。
4.DispatcherServlet根据得到的ModelAndView中的视图对象,找到一个合适的ViewResolver(视图解析器),根据视图解析器的配置,DispatcherServlet将要显示的数据传给对应的视图,最后显示给用户。
springMVC为什么可以是单例的,而struts2必须是多例的?
springMVC的参数都是在方法中传递的,使用的都是局部变量,该变量的生命周期仅仅存在在方法中,方法调用完毕,参数就会
被销毁,所以即使springMVC是单例的,也不会影响到控制层方法的调用,struts2框架之所以必须是多例的,那是因为他使用
了全局变量,通过get和set方法赋值。如果struts2框架是单例的,那么就会造成下一个请求还能看到上一个请求的参数,造成
信息泄露和影响当前请求的正确执行,所以struts2框架要设置为多例的,每次请求创建一个新的对象,新旧对象之间参数互不
影响,这样也就造成了struts2框架对内存的消耗大的问题。
MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手工设置参数以及抽取结果集。 MyBatis 使用简单的 XML 或注解来配置和映射基本体,将接口和 Java 的 POJOs(Plain Old Java Objects, 普通的 Java对象)映射成数据库中的记录。
原理详解:
MyBatis应用程序根据XML配置文件创建SqlSessionFactory,SqlSessionFactory在根据配置,配置来源于两个地方,一处是配置文件,一处是Java代码的注解,获取一个SqlSession。SqlSession包含了执行sql所需要的所有方法,可以通过SqlSession实例直接运行映射的sql语句,完成对数据的增删改查和事务提交等,用完之后关闭SqlSession。
通过configuration加载了hibernate.cfg.xml文件,加载文件后得到sessionFactory,(sessionFactory是线程安全的)
然后通过sessionFactory得到session(session是线程不安全的,相当于jdbc中connection)
通过session操作对象,来操作数据库,最后通过Transaction来进行事务的控制
1.Configuration(类) :
加载配置文件hibernate.cfg.xml文件中的配置信息,从而得到:
1).hibernate的底层信息:
数据库连接,jdbc驱动,方言(dialect),用户名 ,密码
2).hibernate的映射文件(*.hbm.xml)
2.SessionFactory(接口):
通过configuration创建的sessionFactory,可以用来获得session openSession();
sessionFactory是线程安全的,里面保存了数据的配置信息和映射关系
3.Session(接口):
不是线程安全的,相当于jdbc中connection,我们可以使用session来操作数据库负责保存、更新、删除、加载和查询对象,是一个非线程安全的,避免多个线程共享一个session,是轻量级,一级缓存。
4.Transaction(接口):
session.beginTransaction(); //由于Hibernate增删改需要使用事务所以这里要开启事务
session.getTransaction().commit(); //提交
我们一般使用Transaction来进行事务的管理commit(提交)rollback(回滚)
5.Query(接口):
我们一般用来进行数据的查询操作
31.2、get 和 load 的区别,以及注意事项?
1).load是延迟加载(懒加载,按需加载)调用load方法后不会立即发送SQ语句,当访问实体属性的时候才会去发送SQL语句,如果访问的实体不存在,则返回ObjectNotFoundException(对象不存在异常)
2).get 是立即加载,当调用get方法后立即发送sql语句,当访问的实体不存在的时候,则返回null,get(Class entityClass,Serializable id)根据主键加载特定持久化实例。
在程序中一般先用 Assert(ai se te).isTrue断言id是否大于0,若大于0继续执行,若查到数据,则返回实例,否则返回空不同于load,load若有数据则返回实例,否则报出ObjectNotFoundEcception异常,相比来说get效率高些。
注意:
load支持懒加载通过lazy=“true”设置,如果lazy=“false”,则load不再进行懒加载,默认情况下lazy=“true”。
31.2、Mybatis和Hibernate的异同点
相同:
①Hibernate与MyBatis都可以是通过SessionFactoryBuider由XML配置文件生成SessionFactory,然后由SessionFactory 生成Session,最后由Session来开启执行事务和SQL语句。其中SessionFactoryBuider,SessionFactory,Session的生命周期都是差不多的。
②Hibernate和MyBatis都支持JDBC和JTA事务处理。
不同点:
①MyBatis可以进行更为细致的SQL优化,可以减少查询字段。
②MyBatis容易掌握,而Hibernate门槛较高。
③Hibernate的DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射。
④Hibernate对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便。
⑤Hibernate数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL。
⑥Hibernate有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳。
31.3、Hibernate中query对象的常用方法?
query对象.executeUpdate(E ke si Q te) 方法对数据进行修改或者删除
query对象.uniqueResult(u ni ke) 方法返回单条记录,获取唯一结果集
query对象.setFirstResult(fei er si te) 方法设置结果集开始位置,查询开始条数
query对象.setMaxResults() 方法设置每页查询条数
query对象.list() 方法返回查询结果集
31.4、 Hibernate中session 对象的常用方法有?
1.find(String queryString) 查询返回list结果集 ,根据HQL查询字符串来返回实例集合find方法在执行时会先查找缓存,如果缓存找不到再查找数据库,如果再找不到就会返回null。
2.save(Object entity) 添加 保存新的实例
3.saveOrUpdate(); 添加或修改 有id是修改 没id 是更新
4.delete(Object entity); 删除 删除指定的持久化实例
5.update(Object entity); 修改 更新实例的状态 实例必须为持久化状态
6.get(Class entityClass,Serializable id)根据ID查询 根据主键加载特定持久化实例
7.load();根据唯一标识获得 对象 延迟加载
五个hibernate主键生成策略并分别描述?
1).Increment(in ke men te):先查询出最大id,在此基础上加1;hibernate框架会自动处理
2).Sequence(Orade) :oracle 数据库会自动处理
3).Assigned(a sa in de):人工指派(重复插入数据时会违反数据唯一性)
4).native:(数据库本地生成策略,适用多个数据库),适用于mysql.oracle,sqlserver,如果是orade数据库则默认使用的序列名为hibernate-sequence
5).uuid:生成一个32位,不会重复的主键,可以达到真正的跨数据库(通常来说对应的应该是string数据类型)
6).foreign(fo run):通常在一对一主键的时候使用,基于外键的主键生成策略
hibernate是jdbc轻量级的封装,hibernate基于jdbc
jdbc:
jdbc是纯手工的(sql语句,对于结果集的解析)
执行效率高于hibernate,
使用的sql语句,
jdbc是直接操作数据库中的表 select * from 表名
hibernate:
hibernate是一个对JDBC进行轻量级封装的持久层框架
hibernate是全自动的(将常用方法进行了封装,
不用手动进行结果集的解析)
hibernate是跨数据库的,
hibernate的开发效率高于jdbc,
hibernate使用的hql语句,
但是在最后操作数据库是hql语句会转化成sql语句去执行,
hibernate操作的是对象from类名
sql语句中的四种类型以及每种类型的关键字
–DDL数据定义语言: alter修改 create创建 drop删除
–DML数据操作语言: insert新增 delete删除 update修改 select查找
–TCL实务操作语言: commit提交 rollback回滚
–DCL数据操作语言: grant授权 revoke撤销权
jdbc的连接步骤
–导jar包
–加载驱动为数据库连接做准备 关键字:Class.forName
–连接数据库 关键字:DriverManger.getConnection
–写sql语句
–创建数据库操作对象 关键字:preparestatement
–执行sql语句 关键字:executeupdate/executequery
–关闭连接释放资源 关键字:.close
oracle的聚合函数
–count() 统计数量
–max() 最大值
–min() 最小值
–avg() 平均值
–sum() 总和
数学函数–
ABS( ) 绝对值 CEIL() 上取整取整函数>=
FLOOR(fu lao er) 下取整取整函数<= TRUNC( ) 截断函数
ROUND( ) 四舍五入函数
DBMS_RANDOM.VALUE ( [ min,max] )取随机值
转换函数–
to_char( ) 将number,或者date类型转化为字符串
to_number( ) 将字符串转化为数值
to_date( ) 将字符串转化为日期类型
33、xml解析
DOM 、SAX 、JDOM 、DOM4J
Json 解析 (阿里巴巴fastjson、谷歌gson,jackJson)
1、JSON.parse(jsonString): 在一个字符串中解析出JSON对象
2、JSON.stringify(obj) : 将一个JSON对象转换成字符串
3、jQuery.parseJSON(jsonString) : 将格式完好的JSON字符串转为与之对应的JavaScript对象
xml的优点
(1)格式统一
(2)容易与其他系统进行远程交互,数据共享比较方便
xml的缺点
(1)xml文件庞大,文件格式复杂,传输占带宽
(2)服务器和客户端都需要花费大量代码来解析xml,导致服务器和客户端代码变得异常复杂且不易维护
(3)客户端和服务端解析xml花费较多的资源和时间
json的优点
(1)数据格式比较简单,易于读写,格式是压缩的,占用带宽小
(2)易于解析,包括JavaScript可以通过简单的通过eval_r()进行json数据的读取
json的缺点
(1)没有xml那么通用
(2)json格式目前还在推广阶段
JSON与XML区别是什么?
XML它是用于RPC远程调用数据交换格式,因为XML文件格式复杂,比较占宽带,不易于维护,服务器端与客户端解析xml花费较多的资源和时间.
JSON它是用于RPC远程调用数据交换格式,因为JSON文件格式压缩,占宽带小,易于维护。
<1>.在编码方面。
虽然XML和JSON都有各自的编码工具,但是JSON的编码要比XML简单,即使不借助工具,也可以写出JSON代码,但要写出好的XML代码就有点困难;与XML一样,JSON也是基于文本的,且它们都使用Unicode(U ni kou de)编码,且其与数据交换格式XML一样具有可读性。
主观上来看,JSON更为清晰且冗余更少些。JSON网站提供了对JSON语法的严格描述,只是描述较简短。从总体来看,XML比较适合于标记文档,而JSON却更适于进行数据交换处理。
<2>.在解析方面。
在普通的web应用领域,开发者经常为XML的解析伤脑筋,无论是服务器端生成或处理XML,还是客户端用 JavaScript 解析XML,都常常导致复杂的代码,极低的开发效率。
实际上,对于大多数Web应用来说,他们根本不需要复杂的XML来传输数据,XML宣称的扩展性在此就很少具有优势,许多Ajax应用甚至直接返回HTML片段来构建动态Web页面。和返回XML并解析它相比,返回HTML片段大大降低了系统的复杂性,但同时缺少了一定的灵活性。同XML或 HTML片段相比,数据交换格式JSON 提供了更好的简单性和灵活性。在Web Serivice应用中,至少就目前来说XML仍有不可动摇的地位。
FreeMarker是一个用Java语言编写的模板引擎,它是基于模板来生成文本输出的通用工具。Freemarker可以生成HTML, XML,JSP或Java等多种文本输出。
工作原理:定义模板文件,嵌入数据源,通过模板显示准备的数据
(数据 + 模板 = 输出)
我们在使用模板中发现freemarker具有许多优点,它彻底的分离表现层和业务逻辑,模板只负责数据在页面中的表现,不涉及任何的逻辑代码,所以使得开发过程中的人员分工更加明确,作为界面开发人员,只需专心创建HTML文件、图像以及Web页面的其他可视化方面,不用理会数据;而程序开发人员则专注于系统实现,负责为页面准备要显示的数据。
如果使用jsp来展示,开发阶段进行功能调适时,需要频繁的修改JSP,每次修改都要编译和转换,浪费了大量时间,FreeMarker模板技术不存在编译和转换的问题,在开发过程中,我们在不必在等待界面设计开发人员完成页面原型后再来开发程序。由此使用freemarker还可以大大提高开发效率。
悲观锁(Pessimistic Lock), 每次去查询数据的时候都认为别人会修改,所以每次在查询数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了这种锁机制,比如通过select …for update进行数据锁定。
乐观锁(Optimistic Lock), 每次去查询数据的时候都认为别人不会修改, 所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新 这个数据,可以使用版本号,时间戳等机制。乐观锁适用于读比较多的应用类型,这样可以提高吞吐量。
当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。
导致竞态条件发生的代码区称作临界区。
在临界区中使用适当的同步就可以避免竞态条件。
临界区实现方法有两种,一种是用synchronized,一种是用Lock显式锁实现。
Volatile可以看做是一个轻量级的synchronized,它可以在多线程并发的情况下保证变量的“可
见性”(什么是可见性?就是在一个线程的工作内存中修改了该变量的值,该变量的值立即能回
显到主内存中,从而保证所有的线程看到这个变量的值是一致的),它的开销比synchronized小、
使用成本更低,虽说这个Volatile关键字可以解决多线程环境下的同步问题,不过这也是相对的,
因为它不具有操作的原子性,也就是它不适合在对该变量的写操作依赖于变量本身自己。举个最
简单的栗子:在进行计数操作时count++,实际是count=count+1;,count最终的值依赖于它本身
的值。所以使用volatile修饰的变量在进行这么一系列的操作的时候,就有并发的问题
redis五大数据类型(标记理解,认识,必须掌握)?
Redis的键值可以使用物种数据类型:字符串,散列表,列表,集合,有序集合
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
字符串操作:
SET 赋值,用法: SET key value
GET 取值,用法: GET key
INCR 递增数字,仅仅对数字类型的键有用,相当于Java的i++运算,用法: INCR key
INCRBY 增加指定的数字,仅仅对数字类型的键有用,相当于Java的i+=3,用法:INCRBY key increment,意思是key自增increment,increment可以为负数,表示减少。
DECR 递减数字,仅仅对数字类型的键有用,相当于Java的i–,用法:DECR key
DECRBY 减少指定的数字,仅仅对数字类型的键有用,相当于Java的i-=3,用法:DECRBY key decrement,意思是key自减decrement,decrement可以为正数,表示减少。
INCRBYFLOAT 增加指定浮点数,仅仅对数字类型的键有用,用法:INCRBYFLOAT key increment
APPEND 向尾部追加值,相当于Java中的”hello”.append(“ world”),用法:APPEND key value
STRLEN 获取字符串长度,用法:STRLEN key
散列类型(hash):
散列类型相当于Java中的HashMap,他的值是一个字典,保存很多key,value对,每对key,value的值个键都是字符串类型,换句话说,散列类型不能嵌套其他数据类型。一个散列类型键最多可以包含2的32次方-1个字段。
基本命令:
HSET 赋值,用法:HSET key field value
HMSET 一次赋值多个字段,用法:HMSET key field1 value1 [field2 values]
HGET 取值,用法:HSET key field
HMGET 一次取多个字段的值,用法:HMSET key field1 [field2]
HGETALL 一次取所有字段的值,用法:HGETALL key
HEXISTS 判断字段是否存在,用法:HEXISTS key field
HSETNX 当字段不存在时赋值,用法:HSETNX key field value
HINCRBY 增加数字,仅对数字类型的值有用,用法:HINCRBY key field increment
HDEL 删除字段,用法:HDEL key field
HKEYS 获取所有字段名,用法:HKEYS key
HVALS 获取所有字段值,用法:HVALS key
HLEN 获取字段数量,用法:HLEN key
列表类型:
列表类型(list)用于存储一个有序的字符串列表,常用的操作是向队列两端添加元素或者获得列表的某一片段。列表内部使用的是双向链表(double linked list)实现的,所以向列表两端添加元素的时间复杂度是O(1),获取越接近列表两端的元素的速度越快。但是缺点是使用列表通过索引访问元素的效率太低(需要从端点开始遍历元素)。所以列表的使用场景一般如:朋友圈新鲜事,只关心最新的一些内容。借助列表类型,Redis还可以作为消息队列使用。
基本命令:
LPUSH 向列表左端添加元素,用法:LPUSH key value
RPUSH 向列表右端添加元素,用法:RPUSH key value
LPOP 从列表左端弹出元素,用法:LPOP key
RPOP 从列表右端弹出元素,用法:RPOP key
LLEN 获取列表中元素个数,用法:LLEN key
LRANGE 获取列表中某一片段的元素,用法:LRANGE key start stop,index从0开始,-1表示最后一个元素
LREM 删除列表中指定的值,用法:LREM key count value,删除列表中前count个值为value的元素,当count>0时从左边开始数,count<0时从右边开始数,count=0时会删除所有值为value的元素
LINDEX 获取指定索引的元素值,用法:LINDEX key index
LSET 设置指定索引的元素值,用法:LSET key index value
LTRIM 只保留列表指定片段,用法:LTRIM key start stop,包含start和stop
集合类型:
集合在概念在高中课本就学过,集合中每个元素都是不同的,集合中的元素个数最多为2的32次方-1个,集合中的元素师没有顺序的。
基本命令:
SADD 添加元素,用法:SADD key value1 [value2 value3 …]
SREM 删除元素,用法:SREM key value2 [value2 value3 …]
SMEMBERS 获得集合中所有元素,用法:SMEMBERS key
SISMEMBER 判断元素是否在集合中,用法:SISMEMBER key value
SPOP 从集合中随机弹出一个元素,用法:SPOP key
有序集合类型:
有序集合类型与集合类型的区别就是他是有序的。有序集合是在集合的基础上为每一个元素关联一个分数,这就让有序集合不仅支持插入,删除,判断元素是否存在等操作外,还支持获取分数最高/最低的前N个元素。有序集合中的每个元素是不同的,但是分数却可以相同。有序集合使用散列表和跳跃表实现,即使读取位于中间部分的数据也很快,时间复杂度为O(log(N)),有序集合比列表更费内存。
基本命令:
ZADD 添加元素,用法:ZADD key score1 value1 [score2 value2 score3 value3 …]
ZSCORE 获取元素的分数,用法:ZSCORE key value
ZRANGE 获取排名在某个范围的元素,用法:ZRANGE key start stop [WITHSCORE],按照元素从小到大的顺序排序,从0开始编号,包含start和stop对应的元素,WITHSCORE选项表示是否返回元素分数
ZREVRANGE 获取排名在某个范围的元素,用法:ZREVRANGE key start stop(从大到小)
ZCARD 获取集合中元素的个数,用法:ZCARD key
ZCOUNT 获取指定分数范围内的元素个数,用法:ZCOUNT key min max,min和max包含min和max
ZREM 删除一个或多个元素,用法:ZREM key value1 [value2 …]
ZRANK 获取正序排序的元素的排名,用法:ZRANK key value
ZREVRANK 获取逆序排序的元素的排名,用法:ZREVRANK key value
初始化版本库,并提交到远程服务器端
mkdir WebApp
cd WebApp## 标题
git init 本地初始化
touch README
git add README 添加文件
git commit -m ‘first commit’
git remote add origin [email protected]:daixu/WebApp.git
增加一个远程服务器端
上面的命令会增加URL地址为’[email protected]:daixu/WebApp.git’,名称为origin的远程服务器库,以后提交代码的时候只需要使用 origin别名即可