2.2、Spring的AOP两种代理方式?
答:Spring的AOP实现代理的方式有两种,一种是采用JDK动态代理,此种代理方式是Spring的默认代理方式,另外一种则是引入CGLIB类库实现动态代理。
(1)、JDK动态代理:JDK动态代理只能对实现了接口的类生成代理,而不能针对类,因为JDK动态代理是利用反射机制生成一个实现代理接口的匿名类来实现的。
(2)、cglib动态代理:是将代理对象类的class文件加载进来,通过修改其字节码生成子类来实现,因为cglib存在继承关系,所以要被代理的类或方法最好不要声明成final 。
2.3、aop中的通知、连接点、切点、切面,有什么作用?
答:(1)、通知:就是你想要的功能,比如日志、事物等;
(2)、连接点:就是spring允许通知的地方,基本每个方法的前、后,或抛出异常是时都可以是连接点;
(3)、切点:切点用来筛选连接点,选中自己想要干预的方法;
(4)、切面:切面是通知和切入点的结合;
(5)、织入:通过代理把切面应用到目标对象。
2.4、简要描述spring aop的五种同志类型?
答:(1)、前置通知@Before:在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常;
(2)、正常返回通知@AfterRunning:在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行;
(3)、异常返回通知@AfterThrowing:在连接点抛出异常后执行;
(4)、后置通知@After:在连接点执行完成后执行;
(5)、环绕通知@Around :围绕在连接点前后,也就是在切点前后均可执行。
2.5、谈谈Spring中aop切点表达式execution()函数的使用?
答:比如:execution (* com.sample.service.impl..*.*(..))
(1)、第一个*号:表示返回类型,*号表示所有的类型;
(2)、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包;
(3)、第二个*号:表示类名,*号表示所有的类;
(4)、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数;
(5)、多个execution(),则用 ||进行并列,比如execution(* com.edf..*.*(..)) || execution(* com.abc..*.*(..))。
3、解释下Spring的@Qualifier、@Require、@Autowired、@Resource?
答:(1)、@Qualifier:一个类有多个实例时使用 @Quanlifier
注解,可以告诉Spring容器要装配哪个bean;
(2)、@Required:只能设置在setter方法上,进行注解检查 但他只检查属性是否已经设置而不会测试属性是否非空;
(3)、@Autowired:按照类型(byType)装配依赖对象;
(4)、@Resource:默认按照ByName自动注入 ,如果没有找到相同名称的Bean,则会按照byType(类型)进行 匹配。
4、请解释自动装配模式的区别?
答:我在Spring框架中共使用3种方式进行自动装配:
(1)、no:这是Spring框架的默认设置,在该设置下自动装配是关闭的,开发者需要自行在bean定义中用标签明确的设置依赖关系;
(2)、byName:根据bean名称设置依赖关系;
(3)、byType:该选项可以根据bean类型设置依赖关系;
5、Spring实例化Been的三种方式?
答:(1)、使用构造器实例化been,如果构造方法有参数,则使用constructor-arg赋值
:
-
<bean id="" class="">
-
<constructor-arg name="name" value=""/>
-
</bean>
(2)、使用静态工厂方法创建bean,除了需要指定class属性外,还需要通过factory-method属性来指定创建bean实例的工厂方法
值得注意的是factory-method
所指定的方法必须为静态方法:
-
<bean id="" class="" factory-method="静态方法">
-
<constructor-arg name="name" value=""/>
-
</bean>
(3)、使用实例工厂创建been,factory-bean来指定实例化的been名称,factory-method属性来设定要调用的方法
-
<!--实例化工厂实例化bean -->
-
<bean id="instancefactory" class=""/>
-
<bean id="" factory-bean="instancefactory" factory-method="">
-
<constructor-arg name="" value=""/>
-
</bean>
6、SpringMVC
6.1、简单描述SpringMVC工作原理?
答:客户端向web服务器发送一个http请求,web服务器对http请求进行解析,解析后的URL地址如果匹配了DispatcherServlet的映射路径,web容器就将请求交给DispatcherServlet处理;DispatcherServlet接收到这个请求后,再对URL进行解析,得到请求资源标识符,然后调用相应方法得到的HandlerMapping对象,调用这个对象的相应方法获得Handler对象以及它对应的拦截器;在拦截器方法中,提取请求中的数据模型,Handler执行完毕后返回一个ModelAndView对象给DispatcherServlet并渲染成视图返回给客户端。
6.2、如果在拦截请求中,我想拦截get方式提交的方法,怎么配置?
答:以在@RequestMapping注解里面加上method=RequestMethod.GET
6.3、SpringMVC拦截器是怎么实现的?
答:可以通过实现HandlerInterceptor接口实现,重写preHandle()和afterCompletion()方法,preHandle()方法在进入Handler方法之前执行,主要用于身份认证、身份授权等,afterCompletion()方法在执行Handler完成后执行,可以用来处理异常和日志;当然使用aop也可实现。
6.4、拦截器与过滤器的区别 ?
答:(1)、拦截器是基于java的反射机制的,而过滤器是基于函数回调;
(2)、拦截器不依赖与servlet容器,过滤器依赖与servlet容器;
(3)、 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用;
(4)、拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问;
(5)、在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
十二、jdbc和mybatis
1、jdbc的操作流程?
答:(1)、装载驱动Class.forName();
(2)、建立连接DriverManager.getConnection(url, user, password);
(3)、建立预编译对象conn.prepareStatement(sql);
(4)、执行操作
- execute(String sql):运行语句,返回是否有结果集
- executeQuery(String sql):运行select语句,返回ResultSet结果集。
- executeUpdate(String sql):运行insert/update/delete操作,返回更新的行数。
- addBatch(String sql) :把多条sql语句放到一个批处理中。
- executeBatch():向数据库发送一批sql语句执行。
2、#{}和${}的区别是什么?
答:${}是Properties文件中的变量占位符,它可以用于标签属性值和sql内部,属于静态文本替换,能被sql注入,
#{}是sql的参数占位符,Mybatis会将sql中的#{}替换为?号,在sql执行前会使用PreparedStatement的参数设置方法,
按序给sql的?号占位符设置参数值,进行预编译。
3、Xml映射文件中,除了常见的select|insert|updae|delete标签之外,还有哪些标?
答:还有很多其他的标签,<resultMap>、<sql>、<include>、<selectKey>,加上动态sql的9个标签,
trim|where|set|foreach|if|choose|when|otherwise|bind等,其中<sql>为sql片段标签,通过<include>标签引入sql片段,
<selectKey>为不支持自增的主键生成策略标签。
4、最佳实践中,通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?
答:Dao接口,就是人们常说的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中的id值,接口方法内的参数,
就是传递给sql的参数。Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位Dao接口里的方法,是不能重载的,
因为是全限名+方法名的保存和寻找策略。
Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理对象,代理对象会拦截接口方法,转而执行映射的sql,然后将sql执行结果返回。
5、Mybatis动态sql是做什么的?都有哪些动态sql?能简述一下动态sql的执行原理不?
答:Mybatis动态sql可以让我们在Xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能,
Mybatis提供了9种动态sql标签trim|where|set|foreach|if|choose|when|otherwise|bind。
其执行原理为,使用OGNL从sql参数对象中计算表达式的值,根据表达式的值动态拼接sql,以此来完成动态sql的功能。、
6、Mybatis中如何执行批处理?
答:使用BatchExecutor完成批处理。
7、Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
答:Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,
collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。
它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器进行延时加载。
8、Mybatis都有哪些Executor执行器?它们之间的区别是什么?
答:Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。
(1)、SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
(2)、ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,
不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。简言之,就是重复使用Statement对象。
(3)、BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),
等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行
executeBatch()批处理。与JDBC批处理相同,但没有jdbc效率高。
作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。
9、Mybatis中如何指定使用哪一种Executor执行器?
答:在Mybatis配置文件中,可以指定默认的ExecutorType执行器类型,value的值可取SIMPLE、REUSE、BATCH
比如: <setting name="defaultExecutorType" value="SIMPLE" />
10、Mybatis映射文件中,如果A标签通过include引用了B标签的内容,请问,B标签能否定义在A标签的后面,还是说必须定义
在A标签的前面?
答:虽然Mybatis解析Xml映射文件是按照顺序解析的,但是,被引用的B标签依然可以定义在任何地方,Mybatis都可以
正确识别。原理是,Mybatis解析A标签,发现A标签引用了B标签,但是B标签尚未解析到,尚不存在,此时,Mybatis会
将A标签标记为未解析状态,然后继续解析余下的标签,包含B标签,待所有标签解析完毕,Mybatis会重新解析那些被标记
为未解析的标签,此时再解析A标签时,B标签已经存在,A标签也就可以正常解析了。
11、讲下MyBatis的缓存?
答:MyBatis的缓存分为一级缓存和二级缓存, 一级缓存放在session里面,默认就有,二级缓存放在它的命名空间里,默认
是打开的,使用二级缓存属性类需要实现Serializable序列化接口用来保存对象的状态。
十三、WebService
1、WebService到底是什么,请简述其调用原理?
答:WebService是一种跨编程语言和跨操作系统平台的远程调用技术;
WebService的工作调用原理:对客户端而言,我们给WebService客户端API传递wsdl文件的url地址,这些API就会创建出
底层的代理类,我调用这些代理,就可以访问到webservice服务。代理类把客户端的方法调用变成soap格式的请求数据再通过HTTP
协议发出去,并把接收到的soap数据变成返回值返回。对服务端而言,各类WebService框架的本质就是一个大大的Servlet,当远
程调用客户端给它通过http协议发送过来soap格式的请求数据时,它分析这个数据,就知道要调用哪个java类的哪个方法,于是去
查找或创建这个对象,并调用其方法,再把方法返回的结果包装成soap格式的数据,通过http响应消息回给客户端。
2、如何选择采用哪种风格的Web service?SOAP还是REST?
答:要回答这个问题首先要分清楚两种风格的特点,对于SOAP风格的WebService而言,是一个严格定义的信息交换协议,适用于安全性和
事务处理可靠性方面要求比较高的服务中,比如银行账户与账户间的相互转账,而REST风格的WebService相对于SOAP风格更加轻量级,由于
REST是标准的HTTP访问,其适用性更广,web应用,移动应用都可以在不使用第三方库的情况下方便地使用REST接口构造应用程序,
由于REST根据设计原则,接口是无状态的,REST风格的WebService适合无状态CRUD操作。
3、什么是REST风格,什么是SOAP风格?
答:restful风格:就是一种面向资源服务的API设计方式,其特点为统一资源接口 、无状态、易于扩展;
SOAP风格:SOAP 是基于 XML 的简易协议,可使应用程序在 HTTP 之上进行信息交换。
4、SOAP风格的WebService三要素?
答:(1)、UDDI:即统一描述、发现和整合规范。用来注册和查找服务,把web services收集和存储起来,这样当别人访问
这些信息的时候就从UDDI中查找,看有没有这个信息存在;
(2)、SOAP: 基于HTTP协议,采用XML格式,用来传递信息的格式;
(3)、WSDL: 用来描述如何访问具体的服务。
5、你的系统中是否有使用到webservice开发?具体是怎么实现的?遇到过什么问题?
答:。。。回去看看项目整理。。。
十四、netty
http://blog.csdn.net/baiye_xing/article/details/76735113
1.BIO、NIO和AIO的区别?
- BIO:一个连接一个线程,客户端有连接请求时服务器端就需要启动一个线程进行处理。线程开销大。
- NIO:一个请求一个线程,但客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
-
AIO:一个有效请求一个线程,客户端的I/O请求都是由操作系统先完成了再通知服务器应用去启动线程进行处理,
-
BIO是面向流的,NIO是面向缓冲区的;BIO的各种流是阻塞的。而NIO是非阻塞的;BIO的Stream是单向的,而NIO的channel是双向的;
-
NIO的特点:事件驱动模型、单线程处理多任务、非阻塞I/O;
-
在Reactor模式中,首先是事件驱动的,有一个或多个并发输入源,有一个Service Handler,有多个Request Handlers,
这个Service Handler会同步的将输入的请求(Event)多路复用的分发给相应的Request Handler。
2.NIO的组成?
-
Buffer:与Channel进行交互,数据是从Channel读入缓冲区,从缓冲区写入Channel中的
- flip方法 : 反转此缓冲区,将position给limit,然后将position置为0,其实就是切换读写模式
- clear方法 :清除此缓冲区,将position置为0,把capacity的值给limit。
- rewind方法 : 重绕此缓冲区,将position置为0
-
Channel:表示 IO 源与目标打开的连接,是双向的,但不能直接访问数据,只能与Buffer 进行交互;
-
Selector可使一个单独的线程管理多个Channel,open方法可创建Selector,register方法向多路复用器注册通道;
-
NIO的服务端建立过程:Selector.open():打开一个Selector;ServerSocketChannel.open():创建服务端的Channel
bind():绑定到某个端口上。并配置非阻塞模式;register():注册Channel和关注的事件到Selector上;select()轮询拿
到已经就绪的事件。
3.Netty的特点?
- 一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持;
- 使用更高效的socket底层,对epoll空轮询引起的cpu占用飙升在内部进行了处理;
- 采用多种编码/解码支持,对TCP粘包/分包进行自动化处理;
- 可使用接受/处理线程池,提高连接效率;
- 使用单线程串行化的方式,高效的Reactor线程模型;
- 大量使用了volitale、线程安全类的使用、读写锁的使用。
4、TCP 粘包的解决方法?
答:(1)、消息定长;
(2)、包尾增加特殊字符分割;
(3)、将消息分为消息头和消息体。
5、谈谈为什么nio能够实现一个线程处理多个事务?
答:一个selector监听多个通道,nio处理的多个事件都在Selector上进行注册,当有读写或任何事件发生时,可以从
Selector中获得响应的Selectkey,同时从selectkey中可以找到发生的时间和该事件发生的具体的Channel,以获得客户
端发送的请求数据;
十五、RabbitMQ
1、简述RabbitMQ的基本原理?
答:RabbitMQ是MQ产品的典型代表,是一种分布式应用程序的的通信方法,RabbitMQ是典型的消费-生产者模型,其系统最核心
的组件是Exchange和Queue位于服务端,而producer和consumer在应用端。
(1)、Queue的三种形式:设置为持久化的队列,queue中的消息会在server本地硬盘存储一份,防止数据丢失;
设置为临时队列,queue中的数据在系统重启之后就会丢失;
置为自动删除的队列,当不存在用户连接到server,队列中的数据会被自动删除。
(2)、Exchange的三种类型:Direct直接交换器,Exchange会将消息发送完全匹配ROUTING_KEY的Queue;
fanout广播式交换器,Exchange都会将消息转发给所有绑定的Queue;
topic主题交换器,Exchange会将消息转发给和ROUTING_KEY匹配模式相同的所有队列,
其中* 表是匹配一个任意词组,#表示匹配0个或多个词组。
2、简述RabbitMQ的通信过程?
答:(1)、P1生产消息,发送给服务器端的Exchange;
(2)、Exchange收到消息,根据ROUTINKEY,将消息转发给匹配的Queue1;
(3)、Queue1收到消息,将消息发送给订阅者C1;
(4)、C1收到消息,发送确认字符给队列确认收到消息;
(5)、Queue1收到确认字符,删除队列中缓存的此条消息。
3、RabbitMQ的应用场景有哪些?
答:异步处理、应用解耦、流量削峰。
4、请谈谈RabbitMQ在你项目中的使用?
答:在银行的跨行转账业务中,比如A账户向B账户转账,如果A账户扣款成功了,而由于故障B账户到款失败,如果不对A账户的操作
进行回滚,那就麻烦了,此外同一时间如果有大量的人转账汇款,如果采用同步通信,很可能造阻塞,这两种场景就非常适合RabbitMQ。
十六、dubbo
1、谈谈dubbo的工作原理?
答:Dubbo的架构由生产者、消费者、注册中心、监视器、以及服务器的运行容器组成;生产在启动时,向注册中心注册自己提供的服务;消费者启动时,向注册中心订阅自己所需的服务;注册中心返回提供者地址列表给消费者,如果有变更,注册中心将推送变更数据给消费者;消费者,从远程接口列表中,调用远程接口,dubbo会基于负载均衡算法,选一台提供者进行调用,如果调用失败则选择另一台;消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
-
2、dubbo一般使用什么注册中心?还有别的选择吗?
答:一般使用ZooKeeper ,还可以选择Multicast。
3、测试和生产公用一套zookeeper,怎么保证消费不冲突?
答:我项目中测试和生产使用的是两套zookeeper,如果使用一套zookeeper,说白了就是需要限制访问的权限,可以通过dubbo提供的过滤器功能,
设置白名单实现,需要注意的是dubbo会通过setter方式来自动注入其他的bean,无需使用注解。
4、默认使用什么序列化框架,你知道的还有哪些?
答:如果不引入第三方jar包默认使用的是jdk自带的Object序列化,这种序列化的优点是java原生支持,不需要提供第三方的类库,使用比较简单。缺点:无法跨语言,字节数占用比较大,对于对象属性的变化比较敏感。除了原生的使用较多的是JacksonSerialize,该序列化工具需要依赖JackSon类库。
5、同一个服务多个注册的情况下可以直连某一个服务吗?
答:可以因为A接口配置点对点,不影响B接口从注册中心获取列表,在生产环境中只需要在<dubbo:reference>中配置url指向提供者,将绕过注册中心,多个地址用分号隔开。
6、dubbo集群容错怎么做?
答:dubbo集群容错默认情况下使用Failover模式,失败会自动切换,当出现失败,重试其它服务器,可以通过设retries来设置重试次数;Failfast模式,快速失败,只发起一次调用,失败立即报错,这种模式多用于增加记录;Failback模式,失败自动恢复,后台记录失败请求,定时重发,通常用于消息通知操作;Broadcast模式,广播调用所有提供者,逐个调用,任意一台报错则报错,这种模式常用于通知所有提供者更新缓存或日志等本地资源信息,使用非常方便。
十七、sql
1、常用的sql查询语句?
1.1、用一条SQL 语句 查询出每门课都大于80 分的学生姓名?
答:select name from table group by name having min(fenshu)>80;
1.2、删除除了自动编号不同, 其他都相同的学生冗余信息?
答:delete tablename where 自动编号 not in(select min( 自动编号) from tablename group by学号, 姓名, 课程编号, 课程名称, 分数);
1.3、一个叫 team 的表,里面只有一个字段name, 一共有4 条纪录,分别是a,b,c,d, 对应四个球对,现在四个球对进行比赛,用一条sql 语句显示所有可能的比赛组合?
答:select a.name, b.name from team a, team b where a.name < b.name;
十八、linux
1、linux常用命令?
答:cd、cp、ls、man、rm、find、mv、source、less、more、tail、chmod等。
2、查看linux的4到10行?
3、shell脚本
3.1、shell脚本中如何运算?
答:$((运算内容))
3.2、测试文件存在则输出存在,不存在则输出不存在?
答:test -e /filename && echo "存在" || echo "不存在"
其中-e表示该文件名是否存在,-f该文件名是否存在且为文件,-d该文件名是否存在且为目录,
-r该文件是否有读权限,-w该文件是否有写权限,-x该文件是否有可执行权限,
-a两个条件同时存在返回true,-o任何一个条件存在则返回true,!表示取反,
-eq表示两数字相等,-ne表示两数字不相等,-gt左边的数大于右边的数,-lt左边的数小于右边的数。
3.3、如何使用盘端符[]?
答:[ "$x" == "Y" ]指变量x是否等于变量"Y",注意中括号内的空格不能省。
3.4、shell脚本中“if”语法如何嵌套?
答:if [ 条件一 ]; then
条件1成立时执行的东西
elif [条件二]; then
条件2成立时执行的东西
else
条件1和2都不成立时执行的东西
fi
3.5、shell中使用函数要注意什么?
答:因为shell脚本的执行方式是由上而下、由左而右,因此在shell脚本当中的function的设置一定要在程序的最前面。
3.6、shell脚本的默认变量?
答:$0(执行脚本的文件名),$1(第一个参数),$2(第二个参数),$3(第三个参数),$4(第四个参数);$#(代表后接的参数个数),
$@(代表后面接的每一个参数)。
十九、项目实战
1、项目中使用的设计模式,请结合项目回答清楚?
答:我在项目中使用单例模式,主要实现访问人数的计数功能,单例模式采用静态内部类的方式实现,这样既能最大限度的
减小单例类的使用空间,因为不使用根本就不会实例化,其次由于静态属性只在类加载时实例化一次,这样又保证了线程安全,
下面简单写写该单例的实现;
public class SingleTon{
private int count = 0;
private SingleTon(){}
private static class SingleTonInstance{
private static final SingleTon INSTANCE = new SingleTon();
}
public SingleTon getInstance(){
return SingleTonInstance.INSTANCE ;
}
public int getCount(){
count++;
}
}
工厂模式在项目中的使用:我在项目中报表清单功能就使用了,首先我给报表清单设定一个接口,作为抽象工厂角色,其实现类
作为具体工厂角色,具体工厂角色,需要依赖具体的产品角色,依据传入的参数不同工厂角色会去实例化两种产品,客户申请贷款所需的财务报表和统计报表,当然财务报表和统计报表的实现类要分别去实现各自的抽线产品角色接口,在抽象产品角色中去定义各自所需要的功能,比如在财务报表中提供对贷款企业的资产负债率,担保比率,现金流量等进行审查,入录等一系列功能。
装饰器模式:装饰器的价值在于装饰,他并不影响被装饰类本身的核心功能,装饰器模式最核心的两块就是统一接口和装饰器类在项目中我曾使用装饰器模式
去处理日志,当时记得有这么一个需求,要将错误日志转化成json格式,为了不影响其他日志业务,装饰器模式是最好的选择,仍然以原来的接口Logger
为统一接口,写一个装饰器类去实现这个接口,同时写个子类去继承装饰器类,在子类中将错误日志转化成json格式。
2、说说你项目中最有成就感的项目?
答:我项目中最有成就感的项目是豆粒贷,这个项目从架构到编码我都有参与,该项目整体上采用现阶段比较流行的微服务分布式架构,其中贷款的申请,审批,复核为一个微服务,其中贷款流程采用activiti工作流,客户管理为一个微服务,贷后及催收管理为一个微服务,单点登入系统为一个微服务,项目采用nginx进行负载均衡,采用rabbitMQ异步消息进行异步处理,采用dubbo作为rpc服务框架,采用zookeeper作为分布式管理工具,采用redis作为缓存,采用SSM作为业务逻辑处理。
3、说说豆粒贷有哪些表?
答:用户表、角色表、权限表、客户信息表,贷款申请表、合同表、出账表、逾期记录表,黑名单、芝麻分记录表、贷款品种表订单记录表等等。
4、谈谈activiti工作流?
答:activiti工作流拥有23张表,比如部署信息表,流程数据表,以及包含ru的运行时流程表,包含hi的备份数据表等等,当一个流程申请结束,所有包含ru的表将清空,而包含hi的表将获得备份数据,建流程图省略.
5、谈谈nginx是如何实现负载均衡的?
答:nginx是通过反向代理实现负载均衡的,当客户端请求反向代理服务器时,代理服务器会根据设置的调度规则定位到指定的服务器,然后从指定的服务器直接返回内容给客户端,nginx目前支持4种调度,轮询,ip_hash,fair(根据后端服务器响应的时间长短来分配,响应时间越短被分配几率越大),url_hash(按访问的地址url的hash值结果分配, 使每个url定向到指定的服务器).
6、谈谈你是如何保证nginx的高可用性的?
答:项目中我使用了Keepalived作为nginx高可用性的解决方案,nginx一主一备,而Keepalived是通过虚拟路由冗余协议来解决
单点故障的,当keepalived服务正常工作时主节点会不断的向备份节点发送心跳消息,用以告诉备份节点自己还活着,当主节点发生故障时,就无法发送心跳消息,此时备份节点开始调用自身的接管程序,接管主节点的ip资源和服务,当主节点服务恢复时,备份节点又会释放自己接管的资源,恢复到原来的备用角色。
7、为什么选择rabbitMQ?
答:市面上MQ框架非常多,比如rabbitMQ,ActivitiMQ、ZeroMQ等等,我选择rabbitMQ首先是因为他的高可用性,其提供的消息持久化机制,使得即使MQ所在的服务宕机了,消息都不会丢失,而这点zeroMQ不支持,而比较系统吞吐量(TPS)ActivitiMQ远不及rabbitMQ。
8、说说rabbitMQ的工作原理?
答:rabbitMQ是典型的生产者和消费者模式,其核心部分为Exchange和Queue,Exchange类似于网络中的交换机类,一个Exchange可以和多个Queue进行绑定,producer在传递消息的时候,会传递一个ROUTING_KEY,Exchange会根据这个ROUTING_KEY按照特定的路由算法,将消息路由给指定的queue。Exchange主要有三种类型直接路由,即xchange会将消息发送完全匹配ROUTING_KEY的Queue;广播形式fanout,即不管消息的ROUTING_KEY设置为什么,Exchange都会将消息转发给所有绑定的Queue;主题形式(topic),即Exchange会将消息转发和ROUTING_KEY匹配模式相同的所有队列;Queue将消息发送给消费者,消费者收到则返回一个ack给队列,队列则将消息删除,如果队列没有收到反馈,则等到消费者下次连接时,队列会自动重发消息。
9、项目中你是如何使用RabbitMQ的?
答:为了提高响应速度我项目中使用了RabbitMQ作为异步数据处理,当客户贷款申请成功时,我会将贷款信息写入数据库主表,并将信息写入消息队列,同时立即将申请成功的消息反馈给用户,与此同时rabbitMQ通过fanout广播路由方式将消息发送给邮件消费者,给用户发邮件,通知短信消费者,给客户发短信,通知备用数据库消费者,备份数据,通知redis消费者缓存数据;
10、谈谈zookeeper工作原理?
答:zookeeper是一个分布式的应用程序协调服务,其核心为是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab,Zab协议有两种模式,它们分别是恢复模式(recovery选主)和 广播模式(broadcast同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和leader的状态同步以 后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。
11、zk如何选举?
答:当leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的leader,让所有的Server都恢复到一个正确的状态。Zk的选举算法使用ZAB协议:
- 选举线程由当前Server发起选举的线程担任,其主要功能是对投票结果进行统计,并选出推荐的Server;
- 选举线程首先向所有Server发起一次询问(包括自己);
- 选举线程收到回复后,验证是否是自己发起的询问(验证zxid是否一致),然后获取对方提议的leader相关信息(id,zxid),并将这些信息存储到当次选举的投票记录表中;
- 收到所有Server回复以后,就计算出zxid最大的那个Server,并将这个Server相关信息设置成下一次要投票的Server;
- 线程将当前zxid最大的Server设置为当前Server要推荐的Leader,如果此时获胜的Server获得大半的Server票数, 设置当前推荐的leader为获胜的Server,否则,继续这个过程,直到leader被选举出来。
12.master/slave之间如何实现通信?
答:定期扫描和结点监听。
13.节点变多时,结点监听速度为什么会变慢?
答:ZK有1MB 的传输限制,如果有很多节点,ZK启动时相当的慢,需要显著增大 initLimit 和 syncLimit. ZK的数据库完全放在内存中。 大量的节点意味着会占用很多的内存空间。
14.客户端对ServerList的轮询机制是什么?
答:随机,客户端在初始化将所有Server保存在一个List中,然后随机打散,形成一个环。之后从0号位开始一个一个使用。
15.客户端如何正确处理异断开常和session过期?
答:在ZooKeeper中,服务器和客户端之间维持的是一个长连接,在 SESSION_TIMEOUT 时间内,客户端会定时向服务器发送heart_beat,服务器重置下次SESSION_TIMEOUT时间。因此,在正常情况下,Session一直有效,并且zk集群所有机器上都保存这个Session信息,在出现问题情况下,客户端与服务器之间连接断了,这个时候客户端会主动在地址列表中选择新的地址进行连接。
16.是否可以拒绝单个IP对ZK的访问?
答:不可以,可以通过修改防火墙iptables来解决。
17.创建的临时节点什么时候会被删除,是连接一断就删除吗?延时是多少?
答:连接断了之后,ZK不会马上移除临时数据,只有当SESSIONEXPIRED之后,才会把这个会话建立的临时数据移除。
18.ZooKeeper集群中服务器之间是怎样通信的?
答:Leader服务器会和每一个Follower/Observer服务器都建立TCP连接,同时为每个F/O都创建一个叫做LearnerHandler的实体。LearnerHandler主要负责Leader和F/O之间的网络通讯,包括数据同步,请求转发和Proposal提议的投票等。Leader服务器保存了所有F/O的LearnerHandler。
19、ZooKeeper集群是如何性保证高可用性?
答:ZooKeeper通过复制来实现高可用性,只要集合体中半数以上的机器处于可用状态,它就能够提供服务,每个Follower节点的数据都是Leader节点数据的副本,从概念上来说,ZooKeeper它所做的就是确保对Znode树的每一个修改都会被复制到集合体中超过半数的机器上。如果少于半数的机器出现故障,则最少有一台机器会保存最新的状态,那么这台机器就是我们的Leader。
20、一台Zookeeper挂了怎么办?
答:首先由于zookeeper是配置集群,一台zookeeper挂了不会影响整体项目,如果是leader挂了,则重新选举,如果是follwer挂了,则直接被集群舍弃,同时我们也需要重新加入一个node来取代坏的,设置/zookeeper/data/myid下的myid,设置比当前集群所有myid大1,然后变更故障机域名指向新机器的IP地址。
21、zookeeper出现两个leader怎么办,你是怎么解决的?
答:由于某种原因比如网络,导致leader与超过半数的follwer的心跳连接超时,从而重新选举leader,但事实上leader并没有
死,这样就有了两个leader, 这时候client也获得leader切换的消息,但是仍然会有一些延时,这时候整个系统就很混乱可能有一部分client已经通知
到了连接到新的leader上去了,有的client仍然连接在老的leader上,这样会导致系统混乱。避免这种情况其实也很简单,在follwer切换的时候不在检查到老的
leader出现问题后马上切换,而是在休眠一段足够的时间,确保老的leader已经获知变更并且做了相关的shutdown清理工作了然后再注册成新的leader就能避
免这类问题了,这个休眠时间一般定义为与zookeeper定义的超时时间就够了。
22、Dubbo中zookeeper做注册中心,如果注册中心集群都挂掉,发布者和订阅者之间还能通信么?
答:可以的,启动dubbo时,消费者会从zk拉取注册的生产者的地址接口等数据,缓存在本地。每次调用时,按照本地存储的地址进行调用。
23、Dubbo在安全机制方面是如何解决的
答:Dubbo通过Token令牌防止用户绕过注册中心直连,然后在注册中心上管理授权。Dubbo还提供服务黑白名单,来控制服务所允许的调用方。
24、dubbo的负载均衡策略?
答:随机,按权重设置随机概率;轮循存在慢的提供者累积请求问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上;最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差,使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大;一致性Hash,相同参数的请求总是发到同一提供者,当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
25、dubbo的容错机制?
答:Failover 失败自动切换;Failfast 快速失败;Failsafe 失败安全;Failback 失败自动恢复;Forking 并行调用多个服务器,只要一个成功即返回,通常用于实时性要求较高的读操作,需要浪费更多服务资源。
25:工作流中RepositoryService、RuntimeService、TaskService、HistoryService分别表示什么操作
RepositoryService:流程定义和部署对象
RuntimeService:执行管理,包括流程实例和执行对象(正在执行)
TaskService:执行任务相关的(正在执行)
HistoryService:历史管理
IdentityService:Activiti表的用户角色组
3:流程实例和执行对象的区别
* 流程从开始到结束的最大分支,一个流程中,流程实例只有1个
* 执行对象,就是按照流程定义的规则执行一次的操作,一个流程中,执行对象可以有多个
4:流程变量在项目中的作用
* 1:用来传递业务参数,目的就是审核人可以通过流程变量查看申请人的一些审核信息
2:在连线的condition中设置流程变量,用来指定应该执行的连线${message=='重要'}
3:使用流程变量指定个人任务和组任务的办理人#{userID}
5:activiti工作流中,如果一个任务完成后,存在多条连线,应该如何处理?
* 使用流程变量
* 当一个任务完成之后,根据这几条连线的条件和设置流程变量,例如${流程变量的名称=='流程变量的值'},{}符号是boolean类型,判断走哪条连线
6:activiti工作流中,排他网关和并行网关都能执行什么功能
排他网关:分支,通过连线的流程变量,判断执行哪条连线,如果条件不符合,会执行默认的连线离开,注意:只能执行其中的一个流程。
并行网关:可以同时执行多个流程,直到总流程的结束。可以对流程进行分支和聚合,注意:流程实例和执行对象是不一样的
7:分配个人任务的三种方式
*直接给值,在Xxxx.bpmn文件中指定
*流程变量${流程变量的名称}或者#{}
*使用类 监听这个类(实现一个接口),指定任务的办理人(setAssgnee())
8:个人任务和组任务的查询一样吗?
* 不一样
* 都是用TaskService完成(TaskService.createTasQuery)
* 个人任务(taskAssgnee),组任务(taskCandidateUser)
* 数据库存放,个人任务(类型:参与),组任务(类型,参与,候选)
9、Spring的事务
答:@Transactional,注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
(1)、事务传播行为设置propagation,
i、默认为required,如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务;
ii、supports支持当前事务,如果当前没有事务,就以非事务方式执行;
iii、requires_new新建事务,如果当前存在事务,把当前事务挂起;
iv、not_supported以非事务方式执行操作,如果当前存在事务,就把当前事务挂起;
v、never以非事务方式执行,如果当前存在事务,则抛出异常;
Vi、mandatory使用当前的事务,如果当前没有事务,就抛出异常。
(2)、事务的隔离级别isolation
i、default:使用数据库默认的事务隔离级别;
ii、read_uncommitted这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据,这种隔离级别会产生脏读,不可重复读和幻像读;
iii、read_committed保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据,可以防止脏读;
iv、repeatable_read它保证一个事务不能读取另一个事务未提交的数据外,这种事务隔离级别可以防止脏读,不可重复读,但是可能出现幻像读;
v、serializable花费最高代价但是最可靠的事务隔离级别,事务被处理为顺序执行, 除了防止脏读,不可重复读外,还避免了幻像读;
(3)、readOnly默认为false,若设置为true时,并不是不能在事务中进行修改等DML操作,它只是一个“暗示”,提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,这样可以对数据库性能优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力;
(4)、RollbackFor如果类加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚,注意要让自定义的异常继承自RuntimeException,这样抛出的时候才会被Spring默认的事务处理准确处理;
(5)、noRollbackFor指定类不回滚。
10、数据库事务
答:是指单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行,数据库事务有4个特点:acid即原子性、一致性、隔离性、持久性;
i、原子性:原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚;
ii、一致性:一个事务执行之前和执行之后都必须处于一致性状态;
iii、隔离性:隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离;
iV、持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
数据库提供的4种事务级别,由重到轻:
① Serializable (串行化):可避免脏读、不可重复读、幻读的发生,但数据库性能开销大;
② Repeatable read (可重复读):可避免脏读、不可重复读的发生;
③ Read committed (读已提交):可避免脏读的发生;
④ Read uncommitted (读未提交):最低级别,任何情况都无法保证。
11、数据库事务可能引发的问题,以及引发原因?
答:(1)、脏读:脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据;
(2)、不可重复读:不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了;
(3)、幻读:幻读是指查询刚刚修改的数据,会发现还有一些没有修改,其实这行是从另外一个事务中添加的,就好像产生幻觉一样,这就是发生了幻读。
12、存储过程的使用?
答:存储过程适用于报表这种大量SQL的组合,因为放在代码中组合大复杂,而集中放在存储过程中易于维护,还有碰到需要处理事务,回滚等内容 可以考虑采用存储过程,如果考虑移植性和扩展性,不建议采用存储过程,写存储过程一定要有文档对应,否则交互的系统一多,就不知道存储过程做什么的了。
13、说说dubbo的事件处理线程?
答:
事件处理线程说明 如果事件处理的逻辑能迅速完成,并且不会发起新的IO请求,比如只是在内存中记个标识,则直接在IO线程上处理更快,因为减少了线程池调度。 但如果事件处理逻辑较慢,或者需要发起新的IO请求,比如需要查询数据库,则必须派发到线程池,否则IO线程阻塞,将导致不能接收其它请求。 如果用IO线程处理事件,又在事件处理过程中发起新的IO请求,比如在连接事件中发起登录请求,会报“可能引发死锁”异常,但不会真死锁。 Dispatcher all 所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。 direct 所有消息都不派发到线程池,全部在IO线程上直接执行。 message 只有请求响应消息派发到线程池,其它的比如连接、断开事件,心跳等消息,直接在IO线程上执行。 execution 只请求消息派发到线程池,不含响应,响应和其它连接断开事件,心跳等消息,直接在IO线程上执行。 connection 在IO线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池。 ThreadPool fixed 固定大小线程池,启动时建立线程,不关闭,一直持有。(缺省) cached 缓存线程池,空闲一分钟自动删除,需要时重建。 limited 可伸缩线程池,但池中的线程数只会增长不会收缩。(为避免收缩时突然来了大流量引起的性能问题)。配置如:<dubbo:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="100" />
dubbo详细配置参见https://blog.csdn.net/abcde474524573/article/details/53026110
14、谈谈JDK1.7新特性?
答:(1)、数字字面量可以出现下划线;
(2)、switch 语句可以用字符串了;
(3)、泛型实例的创建可以通过类型推断来简化,比如等式左边指明泛型,等式右边就可以不用写了。
15、谈谈JDK1.8新特性?
答:(1)、JDK1.8的接口能够实现方法,只需在方法前加个default参数即可; (2)、jdk1.8的lambda表达式非常强大,比如使用()->就可以轻松实现匿名内部类,map.forEach((K,V)->{业务逻辑});能轻松实现map遍历。
|
16、项目中有没有用过存储过程,怎么用的?
答:在做五级分类风险预警时写过存储过程,五级分类(正常、关注、次级、可疑、损失);
17、谈谈你的项目平安银行小企业金融服务平台?
答:平安银行小企业金融服务平台主要包括,客户管理、贷款申请、审查审批、合同管理、出账管理、贷后管理、担保管理以及统计查询。
(1)、客户管理主要针对个人客户、法人客户、以及合作方客户;其中个人客户需要提供个人姓名、证件类型、证件号码、家庭住址、电话号码等,法人客户需提供组织机构代码、企业工商营业执照、企业性质、注册资本、经营范围、注册资本,公司高管等信息,合作方客户主要是一些担保公司,可以为客户提供担保,客户信息的变更需要与全行的Becif系统进行交互;
(2)、贷款申请一般分为一般贷款业务申请,以及商圈贷款,所谓商圈贷款是指平安银行指定一个区域和指定行业类型指定一个贷款总额度,在区域和行业内的客户可以申请商圈贷款,而一般贷款业务又可以分为抵押类业务(房产抵押),保证类业务(企业担保和自然人担保),质押类业务(权利类质押和动产类质押),信用类业务等;
(3)、审查审批分为资料审查(对贷款申请人所需资料比如年收入证明、资产负债率、经营状况、贷款用途等进行审查,如果资料不全,需进行补件处理),征信审查(对客户的征信状况进行审查,常见的征信机构有芝麻、腾讯、鹏元、前海、拉卡拉等),贷款审批(贷款审批流程,包括机构负责人审批、分 行审批、总行审批)等;
(4)、合同管理主要分为三类合同抵押合同、质押合同、担保合同以及贷款审批通过后生成的贷款合同,合同的目的主要是明确甲乙双方的责任及该享有的权利,比如抵押合同就明确规定债务人未按合同约定按时偿还贷款,行方有权行使抵押权;
(5)、出账管理:当审批通过后,如果要下放到下游核心系统进行放款,小企业系统就要进行出账,出账时会再再次审核贷款人申请资料是否齐全,合同是否生成并签字且加盖印章,同时同时会对出账金额再一次进行评审,复核贷款利率及贷款期限等;
(6)、贷后管理:可以对贷款额度根据客户的还款状及信用或者押品况进行调整,对客户进行五级分类(正常、关注、次级、可以、损失)尽量将损失降到最低。
(7)、担保管理主要是对押品的状况进行管理,主要分为押品登记、押品重估,押品风险预警、押品的冻结/解冻,常见押品分类有房产、设备、有价值的理财产品,承兑汇票,现金,债券,知识产权等;
(8)、统计查询分为灵活统计以及报表清单,所谓灵活统计可以按个人客户或者法人客户分类进行比如查询贷款信息、还款信息、押品信息、审批意见等,报表清单则是从录入、审批、抵押登记、押品管理、贷后变更等功能模块上进行报表统计。
18、线程池
18.1、java的四种线程池?
答:(1)、newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务;
(2)、newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待;
(3)、newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行;
(4)、newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
18.2、ThreadPoolExecutor线程池中的重要属性?
答:(1)、corePoolSize:线程池中的最小线程数量;
(2)、maximumPoolSize:线程池中的最大线程数量;
(3)、keepAliveTime:超过corePoolSize的空闲线程,在多长时间内会被销毁;
(4)、BlockingQueue:被提交的但尚未被执行的任务,BlockingQueue 不接受 null 元素,BlockingQueue 有以下几种实现方式。
SynchronousQueue: SynchronousQueue不保存任务,它总是将任务提交给线程执行,如果没有空闲的进程,则尝试创建新的进程,如果进程已达到maximumPoolSize设置的最大线程数,则执行拒绝策略;
ArrayBlockingQueue:有界任务队列,初始化时需要指定容量大小,如果当前线程数小于核心线程数,则创建新的线程,如果当前线程数大于核心线程数,且
小于阻塞队列容量则插入到任务队列,等待空闲线程执行,如果当前线程数大于核心线程数,且大于阻塞队列容量则创建新的线程执行新任务,如果当前线程数大于最大线程数,则执行拒绝策略;
LinkedBlockingQueue:无界任务队列,当有新任务执行时,如果线程池中线程数小于corePoolSize则创建新的线程,否则进入队列等待。
18.3、为什么要使用线程池?
答:(1)、降低资源消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗;
(2)、提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行;
(3)、提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
18.4、线程池的执行顺序?
答:
- 如果线程数量<=核心线程数量,那么直接启动一个核心线程来执行任务,不会放入队列中。
- 如果线程数量>核心线程数,但<=最大线程数,并且任务队列是LinkedBlockingQeque的时候,超过核心线程数量的任务会放在任务队列中排队。
- 如果线程数量>核心线程数,但<=最大线程数,并且任务队列是SynchronousQueue的时候,线程池会创建新线程执行任务,这些任务也不会被放在任务队列中。这些线程属于非核心线程,在任务完成后,闲置时间达到了超时时间就会被清除。
- 当任务队列是LinkedBlockingQeque,会将超过核心线程的任务放在任务队列中排队。也就是当任务队列是LinkedBlockingDeque并且没有大小限制时,线程池的最大线程数设置是无效的,他的线程数最多不会超过核心线程数。
- 如果线程数量>核心线程数,并且>最大线程数,当任务队列是SynchronousQueue的时候,会因为线程池拒绝添加任务而抛出异常。
- 如果线程数量>核心线程数,但<=最大线程数,并且队列任务是ArraryBlockingQeque时,超过核心线程数的线程会
首先放入队列,如果队列满了,就新建一个线程,如果线程数大于最大线程数,则执行拒绝策略。
18.5、进程和线程的区别?
答:(1)、进程是资源分配和调度的一个独立单元,而线程是CPU调度的基本单元;
(2)、同一个进程中可以包括多个线程,并且线程共享整个进程的资源,一个进程至少包括一个线程;
(3)、线程是轻量级的进程,它的创建和销毁所需要的时间比进程小很多,所有操作系统中执行的功能是创建线程去完成的;
(4)、线程中执行时一般要进行同步和互斥,因为他们共享同一进程的所有资源。
18.6、有没有了解过线程的Future对象?
答:表示异步计算的结果,提供get()方法如有必要,等待计算完成,然后获取其结果;isCancelled()方法如果在任务正常完成前将其取消,则返回 true;
cancel()方法试图取消对此任务的执行;
isDone()
如果任务已完成,则返回 true。
Futrue可以监视目标线程调用的情况,就是说当线程A需要线程B的执行结果,但没必要一直等待线程B执行完,这个时候可以先拿到未来的Future对象,等线程B执行完再来取真实结果,使用Future非常好用,当你调用Future的get()方法以获得结果时,当前线程就开始阻塞,直接调用返回结果;
ExecutorService executors = Executors.线程池的4种类型;使用executors的方法submit(Runnable task)方法将计算结果放入Future对象中即可。
ExecutorService常用方法
invokeAll(Callable<T>>集合)
返回执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表,
submit(Callable<T> task)
提交一个返回值的任务用于执行返回一个Future对象,isShutdown()判断执行程序是否
关闭则返回 true。
如果要获取其他线程运行的结果,可以采用get()但是会一直阻塞,也可以使用isDone(),如果要对其他线程的结果进行时时跟踪,采用CompletionService,它内部添加了阻塞队列,来获取future中的值,其实现为
CompletionService<String> completionService =
new
ExecutorCompletionService<String>(executor);
CompletionService的实现是维护一个保存Future对象的BlockingQueue。只有当这个Future对象状态是结束的时候,才会加入到这个Queue中,take()方法会从Queue中取出Future对象,如果Queue是空的,就会阻塞在那里,直到有完成的Future对象加入到Queue中,
所以,先完成的必定先被取出。这样就减少了不必要的等待时间。
19、Http
19.1、谈谈Http请求行?
答:包括请求方法、请求URL、Http协议及版本;其中请求方法包括get(一般用于获取服务器资源请求的数据会附在URL之后,数据量一般限制在2KB)、post(一般用于修改服务器上资源,POST方法提交的数据则放置在HTTP报文体里,所以POST方法的安全性比GET方法要高)、put(用来传输文件,要求在请求报文的主体中包含文件内容,但是HTTP/1.1的PUT方法自身不带验证机制,任何人都可以上传文件,存在安全问题)、head(获取报文首部,用于确认URI的有效性及资源更新的日期时间等)等.
19.2、谈谈Http请求头?
答:accept:客户端希望接受的数据格式
accept-Charset:浏览器通过这个头告诉服务器,它支持哪种字符集;
accept-encoding:浏览器通过这个头告诉服务器,它支持哪种压缩格式;
accept-language:浏览器通过这个头告诉服务器,它的语言环境;
host:浏览器通过这个头告诉服务器,它想访问哪台主机;
referer:浏览器通过这个头告诉服务器,客户机是哪个页面来的,可用来防盗;
Connection:浏览器通过这个头告诉服务器,请求完后是断开链接还是维持链接,在http1.1协议中,也就是说默认都会发起Keep-Alive的连接请求,即长连接,要关闭则加入"Connection: close ";
cookie:客户端的Cookie就是通过这个属性传给服务端的;
Content-Type代表发送端本次发送的实体数据的数据类型,注意与响应报文的Content-Type没关系。
19.3、谈谈Http请求体?
答:报文协议及版本,状态码;
19.4、谈谈响应报文头?
答:Cache-Control:服务器应用程序软件的名称和版本;
Content-Type:响应正文的类型;
Content-Length:响应正文长度;
Content-Charset:响应正文使用的编码;
Content-Language:响应正文使用的语言;
Set-Cookie:响应头是服务器返回的响应头用来在浏览器种cookie
19.5、TCP三次握手?
答:第一次握手:建立连接时,客户端发送syn包到服务器,并进入等待状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN,同时自己也发送一个SYN包,此时服务器进入同步接收状态;
第三次握手:客户端收到服务器的反馈后,向服务器发送确认包ACK,此包发送完毕,客户端和服务器进入连接状态,完成三次握手。
19.6、TCP四次挥手?
答:第一次挥手:主机1向主机2发送一个FIN
报文段,表示主机1没有数据要发送给主机2了;
第二次挥手:主机2收到了主机1发送的FIN
报文段,向主机1回一个ACK
报文段,此时主机2告诉主机1,“同意”关闭请求;
第三次挥手:主机2向主机1发送FIN
报文段,请求关闭连接;
第四次挥手:主机1收到主机2发送的FIN
报文段,向主机2发送ACK
报文段,主机2收到主机1的ACK
报文段以后,就关闭连接;此时,主机1等待一小段时间后依然没有收到回复,则证明Server端已正常关闭,此时主机1也关闭连接。
20、分布式锁
20.1、谈谈redis的锁?
答:incr、setnx、set;
incr:加锁的思路是, key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 incr 操作进行加一,当其它用户在执行 incr 操作进行加一时,如果返回的数大于 1 ,说明这个锁正在被使用当中;必须与expire一起使用,以防止获取锁的进程挂了,不释放锁;这样存在一个问题,incr与expire不是原子操作,可能执行完incr该进程就挂了;
setnx:加锁的思路是,如果 key 不存在,将 key 设置为 value ,如果 key 已存在,则 SETNX
不做任何动作;问题同上;
set:客户端请求服务器设置key的值,如果设置成功就表示加锁成功,如果返回失败,那么就代表加锁失败,如果执行代码完成,删除锁,redis2.6.12 开始包含了设置过期时间的功能,从而保证了获取获取锁与锁到期时间之间的原子性。
20.2、如何实现分布式锁?
答:在分布式系统中,共享资源互斥访问问题非常普遍,而针对访问共享资源的互斥问题,常用的解决方案就是使用分布式锁,实现分布式锁有三种方式:redis实现分布式锁、zookeeper实现分布式锁、数据库实现分布式锁;
redis实现分布式锁:redis提供了redlock实现分布式锁,原理如下:
(1). 获取当前时间(毫秒数)。
(2). 按顺序依次向N个Redis节点执行获取锁的操作。
(3). 如果获取锁成功的节点数超过半数,则再计算获取锁的时间有没有超过锁过期时间,如果超过了则认为取锁失败。
(4). 如果取锁失败则应该对所有节点进行释放锁的操作。
一般要部署5个以上的节点,才能保证redlock的可靠性,由于要请求所有的节点才能获取锁,通过Future的方式,先并发向所有节点请求,再一起获得响应结果,然后由于必须获取到所有节点中的半数以上,所以可能出现获取锁冲突,结果谁也不能获取到锁。
zookeeper实现分布式锁:最小的节点获得锁,采用zookeeper的临时节点、顺序节点、永久节点、watcher配合实现分布式锁,过程如下:
(1). 创建一个永久节点作为锁目录lock;
(2). 希望获得锁的线程A就在lock目录下,创建临时顺序节点;
(3). 然后尝试获取比自己小的节点,如果不存在,则说明当前线程顺序号最小,获得锁;
(4). 线程B想获取锁,首先判断自己是不是最小节点,如果不是则设置watcher来监听比自己次小的节点;
(5). 线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是最小的节点,获得锁。
数据库实现分布式锁:采用乐观锁,根据版本号来判断更新之前有没有其他线程更新过,如果被更新过,则获取锁失败。
20.3、谈谈三种分布式锁的优缺点?
答:数据库锁:
-
优点:直接使用数据库,使用简单。
-
缺点:分布式系统大多数瓶颈都在数据库,使用数据库锁会增加数据库负担。
缓存锁:
-
优点:性能高,实现起来较为方便,在允许偶发的锁失效情况,不影响系统正常使用,建议采用缓存锁。
-
缺点:通过锁超时机制不是十分可靠,当线程获得锁后,处理时间过长导致锁超时,就失效了锁的作用。
zookeeper锁:
-
优点:不依靠超时时间释放锁;可靠性高;系统要求高可靠性时,建议采用zookeeper锁。
-
缺点:性能比不上缓存锁,因为要频繁的创建节点删除节点。
20.4、谈谈zookeeper的数据模型及watcher?
答:(1)zookeeper的数据模型为znode,主要有三种节点:
-
永久节点:节点创建后,不会因为会话失效而消失;
-
临时节点:与永久节点相反,如果客户端连接失效,则立即删除节点;
-
顺序节点:与上述两个节点特性类似,如果指定创建这类节点时,zk会自动在节点名后加一个数字后缀,并且是有序的。
(2)监视器(watcher):
-
当创建一个节点时,可以注册一个该节点的监视器,当节点状态发生改变时,watch被触发时,ZooKeeper将会向客户端发送且仅发送一条通知,因为watch只能被触发一次。
21、分布式事务
21.1、什么是分布式事务?
答:简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败,本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
21.2、谈谈分布式事务的解决方案?
答:分布式事务要保证在不同的数据库之间保持数据的一致,由于网络等原因可能导致一方操作失败,如何保证事务的原子性,因为远程调用最郁闷的地方就是,结果有3种,成功、失败和超时。 超时的话,成功失败都有可能;
(1)本地事务+消息队列:小企业系统出账,如果小企业出账成功,下游核心系统成功放款,那整个业务正常结束;
如果消息发送超时,需要重新发送,下游核心系统要做成幂等,防止多次重发导致重发放款;如果经过3次重试扔无反馈,则判定下游系崩溃,则对小企业系统的出账进行本地事务回滚;
21.2、什么是幂等,如何在分布式环境下设计幂等?
答:幂等性:就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,举个例子同一笔贷款只能出账一次:
(1)、首先查询出账状态;
(2)、如果已经出账,直接返回结果;
(3)、如果未出账,则出账并且保存流水;
(4)、返回出账结果;
如果返回出账结果出现故障,用户再次发起请求,那么最终结果还是一样的。
21.3、谈谈dubbo微服务分布式事务?
答:采用XA两阶段提交机制实现全局事务提交
XA机制将提交过程分成prepare、commit两个阶段,事务管理模块在prepare服务A的DB事务、服务B的DB事务都成功后,再逐个commit这些DB事务。DB在prepare返回OK后,如果没有收到来自事务管理模块的commit/rollback请求则会一直保留该分支事务的数据。因此,若上述宕机故障出现在prepare阶段,则可以通过将prepare过的分支事务回滚,来达到全局事务回滚;若上述宕机故障出现在commit阶段,后续仍然可以再次commit那些未成功commit的分支事务,最终达到全局事务提交。
21.4、什么是XA两阶段提交协议?
答:XA机制将提交过程分成prepare、commit两个阶段,
第一阶段:
- 事务管理器通知参与该事务的各个资源管理器,通知他们开始准备事务。
- 资源管理器接收到消息后开始准备阶段,写好事务日志并执行事务,但不提交,然后将是否就绪的消息返回给事务管理器。
第二阶段:
- 事务管理器在接受各个消息后,开始分析,如果有任意其一失败,则发送回滚命令,否则发送提交命令。
- 各个资源管理器接收到命令后,执行(耗时很少),并将提交消息返回给事务管理器。
优点:二阶段提交协议为了保证事务的一致性,不管是事务管理器还是各个资源管理器,每执行一步操作,都会记录日志,为出现故障后的恢复准备依据。
缺点:二阶段提交协议的存在的弊端是阻塞,因为事务管理器要收集各个资源管理器的响应消息,如果其中一个或多个一直不返回消息,则事务管理器一直等待,应用程序也被阻塞,甚至可能永久阻塞。
22、项目中如何使用的多线程?
答:(1)、定时向大量的用户发送邮件、发短信;(2)、异步任务:数据库备份数据可能耗时较长,主线程没有必要等待结果返回,可以交给子线程去
做,主线程只需获取子线程最终的返回结果即可。
23、spring的两种代理方式区别?
答:(1)、jdk动态代理,反射机制生成一个实现代理接口的匿名类,而cglib是通过修改其字节码生成子类来处理;
(2)、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
(3)、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
(4)、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换;
在事务处理中的区别:
基于JDK动态代理 ,可以将@Transactional放置在接口和具体类上。
基于CGLIB类代理,只能将@Transactional放置在具体类上。
24、在使用mq+本地事务实现分布式事务时,遇到过什么问题?你是怎么解决的?
答:为了提高代码的运行效率,分布式事务一致性我选择使用最终一致性实现方案,利用本地事务+mq实现,业务流程是这样的出账时本地数据库先记录,然后发消息通知下游系统扣款;最开始的设计方案是这样的先出账再发消息,但是有个问题就是如果本地数据库出账成功,而发消息却一直失败,那么两边数据库的事务无法保持一致性,如果先发消息再到本地出账,同样有问题,如果消息发送成功,而本地出账却一直失败,同样无法保证事务的一致性,由此可见保证事务本地出账和发消息事务的原子性很有必要;后来又设计了一套方案将发消息与本地数据库出账放在同一个事务中实现原子性,如果消息发送失败则本地事务进行回滚,这样有两个很大的问题,一是可能由于某种原因没有收到ack,那么发送方并不知道是消息中间件真的没有收到消息,如果此事进行回滚,而下游系统实际上已经进行出账,二是由于网络原因可能导致发消息很慢,把网络调用放在DB事务里面,可能会因为网络的延时,导致DB长事务,严重的,会block整个DB,这样风险实在是太大了。为了解决上面的问题需要建一张消息记录表,记录表中记录出账信息,以及是否消费的状态默认为未消费,准备一个后台程序,源源不断的把消息表中的message传送给消息中间件。失败了,不断重试重传。允许消息重复,但消息不会丢,顺序也不会打乱,其实如果针对rabbitmq而言,没有必要写个定时任务扫面消息表,因为对于rabbimq而言,没有超时的概念,RabbitMQ仅仅通过Consumer的连接中断来确认该Message并没有被正确处理,所以说如果消费者断开连接时服务器没有收到返回的ack,那么消息就会被重发,同时rabbitmq提供了持久化功能,这样即使服务器挂了,消息也不会丢失;在下游放款时,作为消费者,由于上游的消息可能有重复,消费者需要将自动ack设为手动,以防止,消费者一收到消息即返回ack,而消费者并未消费这种情况,拿到消息后先判断是否处理过,处理过的直接返回ack给消费者业务逻辑处理完成如果没异常则返回ack,生产者收到ack后将消息表状态改为已消费,生产者如果没收到ack,而消费者成功处理了,则重复上述过程,如果消费异常则回滚消费者事务,给后台人员发邮件,进行人工干预。
答:缓存穿透:指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。在流量大时,可能DB就挂掉了;
解决方案:
- 有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层数据库的查询压力。
- 另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
缓存雪崩
缓存雪崩是指在设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,导致所有的查询都落在数据库上,造成了缓存雪崩。
解决方案:
- 在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
- 可以通过缓存reload机制,预先去更新缓存,在即将发生大并发访问前手动触发加载缓存。
- 不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
- 做二级缓存,或者双缓存策略。A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。
缓存击穿
对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。
缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
26、使用setnx作为分布式锁,当其中一台主机挂了,是否能使用del命令直接将锁删除?
答:绝对不能直接删除,在正常的情况下由于redis采用的单进程,不存在同时多个进程拿到锁,但是假如A挂了,当时间到期时如果B获得了锁,那么B可以开始工作,但由于同一时间,可能C执行了删除锁的操作,删除了B获得的锁,那么C也可以开始工作,这时就会出现BC同时获得锁,为了解决这一问题显然不能采用del去删除锁;解决方案是当A挂了,时间到期时不直接删除A的锁,而是通过redis提供的getset命令,由于该命令在给键设置新值时会返回旧值,通过比较锁的旧值是否小于当前时间,可以判断进程是否已获得锁,当A释放锁时,假如另一个进程B检测到锁已超时,并在C之前执行了GETSET操作,那么C的 GETSET 操作返回的是一个大于当前时间的时间戳,这样C就不会获得锁而继续等待。
27、谈谈java类加载过程?
答:加载、验证、准备、解析、初始化这5个阶段,在这五个阶段中,加载、验证、准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始,这是为了支持Java语言的动态绑定,java绑定分两种,静态绑定和动态绑定:
静态绑定:即前期绑定。在程序执行前已经被绑定,java当中的方法只有final,static,private和构造方法是前期绑定的;
动态绑定:即晚期绑定,也叫运行时绑定。在运行时根据具体对象的类型进行绑定。在java中,几乎所有的方法都是后期绑定的。
(1)、加载
加载时类加载的第一个过程,在这个阶段,将完成一下三件事情:
1. 通过一个类的全限定名获取该类的二进制流。
2. 将该二进制流中的静态存储结构转化为方法去运行时数据结构。
3. 在内存中生成该类的Class对象,作为该类的数据访问入口。
(2)、验证
验证的目的是为了确保Class文件的字节流中的信息不回危害到虚拟机.在该阶段主要完成以下四钟验证:
1. 文件格式验证:验证字节流是否符合Class文件的规范,如主次版本号是否在当前虚拟机范围内,常量池中的常量是否有不被支持的类型.
2. 元数据验证:对字节码描述的信息进行语义分析,如这个类是否有父类,是否集成了不被继承的类等。
3. 字节码验证:是整个验证过程中最复杂的一个阶段,通过验证数据流和控制流的分析,确定程序语义是否正确,主要针对方法体的验证。如:方法中的类型转换是否正确,跳转指令是否正确等。
4. 符号引用验证:这个动作在后面的解析过程中发生,主要是为了确保解析动作能正确执行。
(3)、准备
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。对于该阶段有以下几点需要注意:
1、这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在Java堆中。
2、这里所设置的初始值通常情况下是数据类型默认的值,而不是被在Java代码中被显式地赋予的值。
(4)、解析
该阶段主要完成符号引用到直接引用的转换动作。解析动作并不一定在初始化动作完成之前,也有可能在初始化之后。
(5)、初始化
初始化时类加载的最后一步,前面的类加载过程,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的Java程序代码。
28、谈谈使用mysql执行计划优化sql?
答:mysql可以通过explain分析低效sql的执行计划,主要分为:table、type、possible_keys、key、key_len、ref、rows。
(1)、table : 输出结果集的表。
(2)、type:表示 mysql 在表中找到所需行的方式,由好到差依次是const、eq_ref、ref、range、index、all;
- const:使用唯一索引或者主键,返回记录一定是1行记录,出现在要连接过个表的查询计划中;
- eq_ref:只返回一行数据,且这行数据是第二个表的主键或者唯一索引,且必须为not null;
- ref:不像eq_ref那样没有主键和唯一索引的要求,总之,返回数据不唯一的等值查找就可能出现;
- range:用于索引范围扫描,常见于使用>,<,is null,between ,in ,like等运算符的查询中;
- index:索引全表扫描,把索引从头到尾扫一遍;
- all:这个就是全表扫描数据文件,效率最低。
(3)、possible_keys:查询可能使用到的索引都会在这里列出来。
(4)、key:查询真正使用到的索引。
(5)、key_len:用于处理查询的索引长度,key_len只计算where条件用到的索引长度,而排序和分组就算用到了索引,也不会计算到key_len中。
(6)、ref:如果是使用的常数等值查询,这里会显示const,如果是连接查询,这里会显示驱动表的关联字段,如果是条件使用了表达式或者函数,或者条件列发生了内部隐式转换,这里可能显示为func。
(7)、rows:这里是执行计划中估算的扫描行数,不是精确值。
29、多进程log4j日志丢失问题?
答:hellojavaer.iteye.com/blog/977599
--------------------- 本文来自 加速奔跑的蜗牛 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/gongxun344/article/details/79383439?utm_source=copy