赞
踩
目录
原子性:事务中的操作要么全部成功要么全部失败。
一致性:事务前后数据的完整性必须保持一致。
隔离性:多个并发的事务之间是相互隔离的,互不干扰的。
持久性:事务提交后,数据是永久改变的。
SpringBoot是Spring推出用于解决传统框架配置文件冗余,装配组件繁杂的基于Maven的解决方案,旨在快速搭建单个微服务。
版本号:2.1.6
三、什么是redis?
Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。
四、如何理解springcloud微服务项目中,eureka,provider,consumer它们之间的关系?
eureka:提供服务注册于发现
provider:服务提供方法将自身注册到eureka,让消费方找到
consumer:服务消费方从eureka获取注册服务列表,能够消费服务
五、mysql默认的存储引擎是什么?
- Mysql在V5.1之前默认存储引擎是MyISAM;在此之后默认存储引擎是InnoDB。
-
-
-
- MyISAM不支持事务,InnoDB支持事务。
-
- MySIAM不支持外键,InnoDB支持外键,
-
- MySIAM支持全文索引,InnoDB不支持全文索引。
六、什么是跨域?
要了解跨域,先要说说同源策略。所谓同源是指,域名,协议,端口相同,有一个不一样则是跨域。
七、什么是token?
Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌。
当第一次登录后,服务器生成一个Token便将此Token返回给客户端,
以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
八、什么是RESTful?
首先rest是一种API的模式,常以JSON格式编写。符合rest约束风格和原则的应用程序或设计就是RESTful。
九、SpringCloud解决了哪些问题?
与分布式系统相关的复杂性
处理服务发现的能力
解决冗余问题
负载平衡
减少性能问题
十、微服务中什么是熔断?什么是服务降级?
服务熔断的作用是当某服务出现不可用或响应超时的情况时,为了防止整个系统出现雪崩,暂时停止对该服务的调用。
服务降级是为了预防某些功能(业务场景)出现负荷过载或者响应慢的情况,在其内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的fallback(退路)错误处理信息。
十一、微服务的优缺点是什么?
优点:
易于开发和维护:因为一个服务只关注一个特定的业务,业务就变得比较清晰。同时维护起来也是比较方便。
单个服务启动比较快:单个服务代码量不会很多,启动起来就会很快。
便于伸缩:如果系统中有三个服务ABC,服务B的访问量比较大,我们可以将服务B集群部署。
缺点:
运维要求比较高:之前就一个war包,现在一个系统中会有很多的服务,每个服务都对应一个war包,维护起来就会变得很麻烦。
技术复杂性提高:微服务就会带来一系列的问题,事务问题,Session一致性问题,锁问题等。
十二、微服务之间如何独立通讯的?
同步通信:dobbo通过 RPC 远程过程调用、springcloud通过 REST接口json调用等。
异步:消息队列,如:RabbitMq、ActiveMq、Kafka 等。
十三、SpringCloud 和 Dubbo 有哪些区别?
首先,他们都是分布式管理框架。
dubbo 是二进制传输,占用带宽会少一点。SpringCloud是http 传输,带宽会多一点,同时使用http协议一般会使用JSON报文,消耗会更大。
SpringCloud 接口协议约定比较松散,需要强有力的行政措施来限制接口无序升级。
最大的区别:
Spring Cloud抛弃了Dubbo 的RPC通信,采用的是基于HTTP的REST方式。
十四、SpringBoot 和 SpringCloud 之间关系?
SpringBoot:专注于快速方便的开发单个个体微服务(关注微观);
SpringCloud:关注全局的微服务协调治理框架,将SpringBoot开发的一个个单体微服务组合并管理起来(关注宏观);
SpringBoot可以离开SpringCloud独立使用,但是SpringCloud不可以离开SpringBoot,属于依赖关系。
十五、eureka和zookeeper的区别?
eureka和zookeeper都可以提供服务注册与发现的功能,zookeeper 是CP原则,强一致性和分区容错性。eureka 是AP 原则 可用性和分区容错性。
zookeeper当主节点故障时,zk会在剩余节点重新选择主节点,耗时过长,虽然最终能够恢复,但是选取主节点期间会导致服务不可用,这是不能容忍的。
eureka各个节点是平等的,一个节点挂掉,其他节点仍会正常保证服务。
十六、mycat是什么?你们公司分库分表的分片规则是什么?
Mycat是基于MySQL的数据库中间件,用来协调切分后的数据库,使其可以进行统一管理。
分片规则:取模分片 PartitionByMode
十七、什么是集合?
集合有两个父接口,一个collection,一个Map;
而collection有两个子接口,一个List,一个Set;
List有两个常见的实现类 ArrayList,LinkedList;
Set有两个常见的实现类 HashSet,TreeSet;
Map有两个常见的实现类 HashMap,HashTable。
十八、什么是dubbo?
Dubbo和Springcloud都是分布式服务中常用的框架。dubbo与cloud不同,dubbo基于RPC协议。它提供了三大核心功能: 面向接口的远程方法调用,
智能容错和负载均衡, 以及服务自动注册和发现。
十九、什么是spring?
Spring是一个开源的轻量级的Java开发框架。是一种简化应用程序的开发。
- 在spring出来之前,service层调用dao层都是用new的方式,在spring出来之后,service层和到dao层都会放在spring容器去管理,
-
- 这是spring的第一种特性,我们称之为IOC,控制反转。
- spring还有一种特性,我们称之为AOP,大白话,所谓“面向切面”,说白了就是专门的人干专门的事。
-
- 在项目很多公有的或是要被重复被调用的模块可以被抽取出来,利用的就AOP的特性,例如日志模块。
一、SSM的开发步骤是什么?
"二八"
web.xml
1.前端控制器DispatcherServlet
2.过滤器CharacterEncodingFilter
applicationContext.xml
1.扫描包 <context:component-scan base-package="cn"/>
2.mvc驱动 <mvc:annotation-driven/>
3.事务驱动 <tx:annotation-driven transaction-manager="txManager"/>
4.配置数据源
5.SqlSessionFactoryBean
6.配置事务
7.数据映射器
8.视图解析器
二、设计一个权限模块?
- 权限模块是数据库的安保系统,决定谁可以使用,谁拥有权限,有user、role、permission表,通过user-role、role-permission关联表(码表)进行关联,
-
- 给角色分配权限,角色发放权限给用户。
三、什么是docker?
- Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows 机器上,
-
- 也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。
四、什么是dockfile?
Dockerfile是一个包含用于组合映像的命令的文本文档,可以使用在命令行中调用任何命令。
五、什么是docker-compose?
- 可以管理多个 Docker 容器组成一个应用,定义一个 YAML 格式的配置文件docker-compose.yml,写好多个容器之间的调用关系。
-
- 然后,只要一个命令,就能同时启动/关闭这些容器。
Portainer
是Docker的图形化管理工具
七、在公司里使用的docker的版本是什么?
18.09.7
八、镜像和容器的关系?
容器是可写的,镜像是可读的。容器是镜像的实例,镜像是容器的静态备份。
九、分布式锁中的基于redis的setnx的原理以及set和setnx的区别是什么?
分布式锁基于setnx原理,如果 SETNX 返回1,说明该进程获得锁。
如果 SETNX 返回0,说明其他进程已经获得了锁,进程不能进入临界区。进程可以在一个循环中不断地尝试 SETNX 操作,以获得锁。
redis中set是将字符串值 value 关联到 key 。如果 key 已经持有其他值, SET 就覆写旧值,无视类型。
setnx是当且仅当 key 不存在时,可以设置 value值 。若给定的 key 已经存在,则 SETNX 不做任何动作。
十、什么是springcloud,springcloud中的组件有哪些?分别描述下它的原理?
SpringCloud架构中最核心的是Eureka注册中心,它是一个通讯录。所有p端和c端都会注册在上面,cloud中c端调用p端应用feign组件进行调用。应用程序导致网络请求出现问题时,hystrix组件实现容错机制。用户请求量变大是,用ribbon组件实现客户端的负载均衡。而服务器端的负载均衡通常使用nginx反向代理。SpringCloud中有类似于servlet过滤器的组件,zuul网关组件对整个服务架构进行安全过滤。当使用一个服务调用多个feign组件的时候,sleuth+zipkin组件用来服务追踪,可以用来调试接口(追踪问题)。config组件进行统一配置。
dubbo基于RPC,SpringCloud基于RESTful。
组件ribbon、feign、hstrix 、eureka、zuul。
十一、什么是jmeter?
- Apache JMeter 是 Apache 组织开发的基于 Java 的压力测试工具。用于对软件做压力测试,它最初被设计用于 Web 应用测试,但后来扩展到其他测试领域。
-
- 发送请求到服务端,获取目标服务的统计信息,生成不同格式的报告。
1.描述一下你上一家公司SSM项目的功能模块有哪些?
2.描述一下你上一家公司dubbo+zookeeper项目的功能模块有哪些?
3.描述一下你上一家公司springcloud项目的功能模块有哪些?
4.描述一下你上一家公司项目中权限模块的思路?
5.描述一下上一家公司项目中的报表模块的思路?
一、什么是mybatis?
一种持久层框架,类似于JDBC访问数据库的操作,
我们说JDBC使用到的对象有Connection对象,PreparedStatement对象,ResultSet对象。
而Mybatis框架的核心对象有SqlSessionFactoryBuilder对象,SqlSessionFactory对象,SqlSession对象。
并且myibatis框架和hibernate框架最大的区别就在于mybatis它的灵活性比较高。
二、什么是spring?
在spring出来之前,service层调用dao层都是用new的方式。
在spring出来之后,service层和dao层都会放在spring容器去管理,这是spring的第一种特性,我们称之为IOC,控制反转。
spring还有一种特性,我们称之为AOP,大白话,“面向切面”,说白了就是专门的人干专门的事。在项目很多公有的或是
要被重复被调用的模块可以被抽取出来,利用的就是AOP的特性,例如日志模块。
三、什么是SpringMVC?
当用户发送请求到springmvc中的前端控制器中,通过映射器和适配器返回ModelAndView对象到客户端。这就是SpringMVC的基本原理。
SSM搭建的版本有很多,例如有一个版本可以这么搭建,两个核心配置文件web.xml,applicationContext.xml。
1.前端控制器DispatcherServlet
2.过滤器CharacterEncodingFilter
applicationContext.xml
1.扫描包 <context:component-scan base-package="cn"/>
2.mvc驱动 <mvc:annotation-driven/>
3.事务驱动 <tx:annotation-driven transaction-manager="txManager"/>
4.配置数据源
5.SqlSessionFactoryBean
6.配置事务
7.数据映射器
8.视图解析器
五、什么是集合?
集合有两个父接口,一个Collection,一个Map,而Collection有两个子接口,一个List,一个Set,List有两个常见的实现类ArrayList,LinkedList,
Set有两个常见的实现类HashSet,TreeSet,Map有两个常见的实现类HashMap,HashTable
六、什么是SpringCloud?
SpringCloud架构中最核心的是Eureka注册中心,它是一个通讯录。所有p端和c端都会注册在上面,cloud中c端调用p端应用feign组件进行调用。应用程序导致网络请求出现问题时,hystrix组件实现容错机制。用户请求量变大时,用ribbon组件实现客户端的负载均衡。而服务器端的负载均衡通常使用nginx反向代理。SpringCloud中有类似于servlet过滤器的组件,zuul网关组件对整个服务架构进行安全过滤。
dubbo基于RPC,SpringCloud基于RESTful。
七、什么是Dubbo?
Dubbo和Springcloud都是分布式服务中常用的框架。dubbo与cloud不同,dubbo基于RPC协议。它提供了三大核心功能: 面向接口的远程方法调用,
智能容错和负载均衡, 以及服务自动注册和发现。
Dubbo按照分层的方式来架构,可以最大限度地松耦合。Dubbo采用一种非常简单的模型, 要么提供方提供服务, 要么是消费方消费服务, 所以基于这一点可以抽象出服务提供方和服务消费方两个角色。
八、什么是消息中间件?
消息中间件利用高效可靠的消息传递机制进行异步的数据传输,并基于数据通信进行分布式系统的集成。通过提供消息队列模型和消息传递机制,可以在分布式环境下扩展进程间的通信。
九、eureka和zookeeper的区别?
首先他们都是服务注册和发现的功能,cloud一般选择eureka作为注册中心,dubbo一般选择zookeeper作为注册中心。基于CAP原则,eurek是AP原则,即可用性和分区容错性,zookeeper是CP原则,即强一致性和分区容错性。
十、分布式锁的概念?
在分布式系统中,由于分布式系统的分布性,即多线程和多进程并且分布在不同机器中,这两种锁将失去原有锁的效果,需要我们自己实现分布式锁——分布式锁。
在多线程的环境下,多个线程同时访问同一个资源,导致线程不安全,使用分布式锁来解决这个线程安全问题。
分布式抢购用到分布式锁,上锁;第二个用户要等待第一个用户完成才能进行抢购;利用setnx设置解决安全性问题,缺点是性能过低。
死信、延迟、重试队列
#死信队列
DLQ(Deal Letter Queue),死信队列。当一个消息在队列中变成死信之后,他能被重新发送到 DLQ 中,与 DLQ 绑定到队列就是死信队列。
#什么情况下需要死信队列
·消息被拒绝
·消息过期
·队列达到最大长度
生产者生产一条消息,存储到普通队列中;设置队列的过期时间为 10 秒,在 10 秒内没有消费者消费消息,那么判定消息过期;此时如果设置了死信队列,过期消息被丢给死信队列交换机,然后被存储在死信队列中。
#延迟队列
顾名思义就是延迟执行消息,比如我们可以增加一个队列并设置其超时时间为 10 秒并且不设置任何消费者,等到消息超时,我们可以将消息放入死信队列,让消费者监听这个死信队列就达到了延迟队列的效果。
#重试队列
重试的消息在延迟的某个时间点(业务可设置)后,再次投递给消费者。而如果一直这样重复消费都持续失败到一定次数,就会投递到死信队列,最后需要进行人工干预。
双亲委派模型
#类加载器
#加载类的开放性
类加载器(ClassLoader)是 Java 语言的一项创新,也是 Java 流行的一个重要原因。在类加载的第一阶段“加载”过程中,需要通过一个类的全限定名来获取定义此类的二进制字节流,完成这个动作的代码块就是 类加载器。这一动作是放在 Java 虚拟机外部去实现的,以便让应用程序自己决定如何获取所需的类。
虚拟机规范并没有指明二进制字节流要从一个 Class 文件获取,或者说根本没有指明从哪里获取、怎样获取。这种开放使得 Java 在很多领域得到充分运用。
#类加载器与类的唯一性
类加载器虽然只用于实现类的加载动作,但是对于任意一个类,都需要由加载它的类加载器和这个类本身共同确立其在 Java 虚拟机中的 唯一性。通俗的说,JVM 中两个类是否“相等”,首先就必须是同一个类加载器加载的,否则,即使这两个类来源于同一个 Class 文件,被同一个虚拟机加载,只要类加载器不同,那么这两个类必定是不相等的。
这里的“相等”,包括代表类的 Class 对象的 equals() 方法、isAssignableFrom() 方法、isInstance() 方法的返回结果,也包括使用 instanceof 关键字做对象所属关系判定等情况。
#双亲委派模型
类加载器种类
从 Java 虚拟机的角度来说,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用 C++ 语言实现(HotSpot 虚拟机中),是虚拟机自身的一部分;另一种就是所有其他的类加载器,这些类加载器都有 Java 语言实现,独立于虚拟机外部,并且全部继承自 java.lang.ClassLoader。
从开发者的角度,类加载器可以细分为:
除此之外,还有自定义的类加载器,它们之间的层次关系被称为类加载器的 双亲委派模型。该模型要求除了顶层的启动类加载器外,其余的类加载器都应该有自己的父类加载器,而这种父子关系一般通过组合(Composition)关系来实现,而不是通过继承(Inheritance)。
双亲委派模型
#双亲委派模型过程
某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
使用双亲委派模型的好处在于 Java 类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类 java.lang.Object ,它存在在 rt.jar 中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的 Bootstrap ClassLoader 进行加载,因此 Object 类在程序的各种类加载器环境中都是同一个类。相反,如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个 java.lang.Object 的同名类并放在 ClassPath 中,那系统中将会出现多个不同的 Object 类,程序将混乱。因此,如果开发者尝试编写一个与 rt.jar 类库中重名的 Java 类,可以正常编译,但是永远无法被加载运行。
#双亲委派模型的系统实现
在 java.lang.ClassLoader 的 loadClass() 方法中,先检查是否已经被加载过,若没有加载则调用父类加载器的 loadClass() 方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父加载失败,则抛出 ClassNotFoundException 异常后,再调用自己的 findClass() 方法进行加载。
注意,双亲委派模型是 Java 设计者推荐给开发者的类加载器的实现方式,并不是强制规定的。大多数的类加载器都遵循这个模型,但是 JDK 中也有较大规模破坏双亲模型的情况,例如线程上下文类加载器(Thread Context ClassLoader)的出
现
该问题就是服务集群因为网络震荡导致的多主多从问题,解决方案就是设置服务切换的超时时间,但也同时会导致无法达到高可用的要求。
MySQL 优化
#神器 EXPLAIN 语句
EXPLAIN 显示了 MySQL 如何使用索引来处理 SELECT 语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句。
使用方法,在 SELECT 语句前加上 EXPLAIN 即可,如:
EXPLAIN SELECT * FROM tb_item WHERE cid IN (SELECT id FROM tb_item_cat)
system: 表仅有一行(=系统表)。这是 const 联接类型的一个特例。
possible_keys:
指出 MySQL 能使用哪个索引在该表中找到行
概述
以下列出两点重要特性:
新特性
小栗子
package com.funtl.jdk8.feature.lambda;
import java.util.Arrays;import java.util.List;import java.util.stream.Collectors;
/**
* Lambda 基本用法
* <p>Title: BaseLambda</p>
* <p>Description: </p>
*
* @author Lusifer
* @version 1.0.0
* @date 2019/1/6 10:42
*/public class BaseLambda {
public static void main(String[] args) {
testForeach();
testStreamDuplicates();
}
/**
* Lambda 遍历
*/
public static void testForeach() {
// 定义一个数组
String[] array = {
"尼尔机械纪元",
"关于我转生成为史莱姆这件事",
"实力至上主义教师",
"地狱少女"
};
// 转换成集合
List<String> acgs = Arrays.asList(array);
// 传统的遍历方式
System.out.println("传统的遍历方式:");
for (String acg : acgs) {
System.out.println(acg);
}
System.out.println();
// 使用 Lambda 表达式以及函数操作(functional operation)
System.out.println("Lambda 表达式以及函数操作:");
acgs.forEach((acg) -> System.out.println(acg));
System.out.println();
// 在 Java 8 中使用双冒号操作符(double colon operator)
System.out.println("使用双冒号操作符:");
acgs.forEach(System.out::println);
System.out.println();
}
/**
* Stream 去重复
* String 和 Integer 可以使用该方法去重
*/
public static void testStreamDuplicates() {
System.out.println("Stream 去重复:");
// 定义一个数组
String[] array = {
"尼尔机械纪元",
"尼尔机械纪元",
"关于我转生成为史莱姆这件事",
"关于我转生成为史莱姆这件事",
"实力至上主义教师",
"实力至上主义教师",
"地狱少女",
"地狱少女"
};
// 转换成集合
List<String> acgs = Arrays.asList(array);
// Stream 去重复
acgs = acgs.stream().distinct().collect(Collectors.toList());
// 打印
acgs.forEach(System.out::println);
}}
概述
任何执行 JavaScript 的环境称之为 执行上下文,默认情况下,执行上下文是全局的,这意味着如果代码作为简单函数调用的一部分执行,则该 this 变量将引用 全局对象 。在浏览器的情况下,全局对象是 windows 对象。但在 NodeJS 环境中,this 值是一个特殊的 global 对象。
简单函数
// 案例 1,简单函数,浏览器下的 this -> window,NodeJS 下的 this -> global
function simple1() {
console.log("案例 1,简单函数");
console.log("我是简单函数,this 指向 window");
console.log(this);
}
simple1();
严格模式
// 案例 2,严格模式,this -> undefined
function simple2() {
'use strict';
console.log("案例 2,严格模式");
console.log("我是严格模式,this 指向 undefined");
console.log(this);
}
simple2();
#构造函数
当我们调用 new User,JavaScript 会在 User 函数内创建一个新对象并把它保存为 this 。接着,name, age 和 info 属性会被添加到新创建的 this 对象上。
// 案例 3,构造函数,此时在执行上下文中创建了 this 对象,并在这个 this 对象中增加了 name, age, info 三个属性
function User(name, age) {
this.name = name;
this.age = age;
this.info = function () {
console.log("我是 User 对象中的 info 属性")
console.log(`${this.name} ${this.age}`);
};
console.log("案例 3,构造函数");
console.log("我是 User 对象,我在执行上下文中创建了 this 对象,并增加了 name, age, info 三个属性");
console.log(this);
}
let andy = new User('Andy', 22);
andy.info();
简单对象 1
当某个属性被发起普通函数调用时,则 this 指向 window 对象
// 案例 4,简单对象
let user = {
name: '小明',
age: userAgeFunction,
sex: function () {
console.log("我是 user 对象的 sex 属性,this 指向 user 对象,而不是全局对象");
console.log(this);
},
talk: function () {
console.log(this);
}
};
function userAgeFunction() {
console.log("我是 user 对象的 age 属性,this 指向 user 对象,而不是全局对象");
console.log(this);
}
console.log("案例 4,简单对象");
user.age();
user.sex();
let talk = user.talk;
console.log("我是 talk,我把 user 对象的 talk 属性当普通函数调用了,所以这里的 this 指向 window");
talk();
简单对象 2
age 函数内部, this 指向 lee 对象,但是,在 innerAge 函数内部,this 指向全局对象,规则告诉我们无论何时一个普通函数被调用时,那么 this 将指向全局对象。
// 案例 5,简单对象 2
let lee = {
name: 'lee',
birth: 1998,
age: function () {
console.log("我是 lee 对象的 age 属性,this 指向 lee 对象,而不是全局对象");
console.log(this);
function innerAge() {
console.log("我虽然是 lee 对象中的 age 属性中的 innerAge() 函数,但我会被普通调用,所以这里的 this 指向 window,而不是 lee 对象");
console.log(this);
}
// 这里发起 lee 对象 age 属性内部的 innerAge() 普通调用,所以该函数中的 this 指向 window
innerAge();
}
};
console.log("案例 5,简单对象");
lee.age();
call、apply、bind
function User(username, password) {
this.username = username;
this.password = password;
this.displayUser = function() {
console.log(`User: ${this.username} ${this.password}`);
}
}
let user1 = new User("admin", "123456");
let user2 = new User("guest", "abcdef");
console.log("案例 6,call apply bind");
console.log("我是 user1 对象");
user1.displayUser();
console.log("我是 user2 对象")
user2.displayUser();
console.log("我是 user1 对象,我使用了 call 方法将我的 this 指向了 user2")
user1.displayUser.call(user2);
console.log("我是 user1 对象,我使用了 apply 方法将我的 this 指向了 user2")
user1.displayUser.apply(user2);
console.log("我是 user1 对象,我使用了 bind 方法将我的 this 指向了 user2 并返回了一个新的方法")
let user2Display = user1.displayUser.bind(user2)
user2Display()
什么是token?
token是服务端生成的一串字符串,目的是作为客户端进行请求的一个令牌。当第一次登录后,服务器生成一个token(一串字符串),并将此token返回给客户端,此后页面接收到请求后,只需要找到token即可获取信息,无需再输入登录名和密码。
token一般用于验证表明身份的数据或是别的口令数据;token可以用url传参,也可以是post提交,也可以夹在http的header中。
什么是session?
session从字面上讲就是会话。
为了让服务器知道当前请求是从哪里发过来的,所以服务器给每个客户端分配了不同的“身份标识”(类似id),之后客户端每次发送请求都携带上这个标识(id),这样做,服务端就可以知道是谁发送的请求。
session和token的区别:
服务器在使用session把用户信息临时保存在服务器上,用户离开网站后session就会被销毁。
但session有一个缺陷:如果web服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时候session会丢失。
而token最大特点就是支持跨平台操作,不论是在App还是在PC端,token都可以保留。
1.API管理
原理
在SpringMVC中RequestMappingHandlerMapping是比较重要的一个角色,它决定了每个URL分发至哪个Controller。
Spring Boot加载过程如下,所以我们可以通过自定义WebMvcRegistrationsAdapter来改写RequestMappingHandlerMapping。
Hystrix的熔断
Netflix’ Hystrix是第一个专门用于熔断的服务中间件。当它在2012年向公众发布,以提供“对延迟和失败有更大容忍度”的微服务架构时,Netflix已经在内部广泛使用了一年多的时间了。根据这个项目的描述,Hystrix一直是Netflix服务中间件的基本组成部分之一,直到2018年底进入维护模式,这标志着“[关注点]转向更适应应用程序实时性能的实现,而不是预先配置的设置。”
Hystrix是一个Java库,开发人员可以使用它用熔断逻辑封装服务调用。它基于阈值,可以立即判定调用失败并执行回滚逻辑,具体参考第一部分。除了提供超时和并发限制之外,它还可以向监视工具发布度量metrics。最后,当与Archaius库一起使用时,它还可以支持动态配置更改。
服务网格中的熔断
Istio是一个服务网格,它支持基于连接池、每个连接的请求和故障检测参数的熔断。它是在所谓的“目的地规则(destination rules)”的帮助下做到这一点的,该规则告诉每个Envoy代理应用于通信的策略是什么,以及如何应用。这个步骤发生在路由之后,然而这并不总是理想的。目标规则可以指定负载均衡的限制、连接池大小以及最终符合“异常值”条件的参数,以便可以从负载均衡池中删除不健康的实例。这种类型的熔断擅长于使客户端免受服务端故障的影响,但是由于目标规则总是在集群范围内应用,所以它缺乏一种方法来将断路器限制在客户端的一个子集内。为了实现断路器与服务质量模式(quality-of-service)的组合,必须创建多个客户机子集的路由规则,并且每个子集都有自己的目标规则。
3.微服服务跟踪
服务追踪系统实现
上面是服务追踪系统架构图,你可以看到一个服务追踪系统可以分为三层。
数据采集层,负责数据埋点并上报。
数据处理层,负责数据的存储与计算。
数据展示层,负责数据的图形化展示
服务追踪的作用
第一,优化系统瓶颈。
通过记录调用经过的每一条链路上的耗时,我们能快速定位整个系统的瓶颈点在哪里。比如你访问微博首页发现很慢,肯定是由于某种原因造成的,有可能是运营商网络延迟,有可能是网关系统异常,有可能是某个服务异常,还有可能是缓存或者数据库异常。通过服务追踪,可以从全局视角上去观察,找出整个系统的瓶颈点所在,然后做出针对性的优化。
第二,优化链路调用。
通过服务追踪可以分析调用所经过的路径,然后评估是否合理。比如一个服务调用下游依赖了多个服务,通过调用链分析,可以评估是否每个依赖都是必要的,是否可以通过业务优化来减少服务依赖。
此外,一般业务都会在多个数据中心都部署服务,以实现异地容灾,这个时候经常会出现一种状况就是服务 A 调用了另外一个数据中心的服务 B,而没有调用同处于一个数据中心的服务 B。跨数据中心的调用视距离远近都会有一定的网络延迟,像北京和广州这种几千公里距离的网络延迟可能达到 30ms 以上,这对于有些业务几乎是不可接受的。通过对调用链路进行分析,可以找出跨数据中心的服务调用,从而进行优化,尽量规避这种情况出现。
第三,生成网络拓扑。
通过服务追踪系统中记录的链路信息,可以生成一张系统的网络调用拓扑图,它可以反映系统都依赖了哪些服务,以及服务之间的调用关系是什么样的,可以一目了然。除此之外,在网络拓扑图上还可以把服务调用的详细信息也标出来,也能起到服务监控的作用。
第四,透明传输数据。
除了服务追踪,业务上经常有一种需求,期望能把一些用户数据,从调用的开始一直往下传递,以便系统中的各个服务都能获取到这个信息。比如业务想做一些 A/B 测试,这时候就想通过服务追踪系统,把 A/B 测试的开关逻辑一直往下传递,经过的每一层服务都能获取到这个开关值,就能够统一进行 A/B 测试。
服务追踪系统原理
它的核心理念就是调用链:通过一个全局唯一的 ID 将分布在各个服务节点上的同一次请求串联起来,从而还原原有的调用关系,可以追踪系统问题、分析调用数据并统计各种系统指标。
可以说后面的诞生各种服务追踪系统都是基于 Dapper 衍生出来的,比较有名的有 Twitter 的Zipkin、阿里的鹰眼、美团的MTrace等。(服务追踪系统的鼻祖:Google 发布的一篇的论文Dapper, a Large-Scale Distributed Systems Tracing Infrastructure,里面详细讲解了服务追踪系统的实现原理。)
要理解服务追踪的原理,首先必须搞懂一些基本概念:traceId、spanId、annonation 等。Dapper 这篇论文讲得比较清楚,但对初学者来说理解起来可能有点困难,美团的 MTrace 的原理介绍理解起来相对容易一些,下面我就以 MTrace 为例,给你详细讲述服务追踪系统的实现原理。虽然原理有些晦涩,但却是你必须掌握的,只有理解了服务追踪的基本概念,才能更好地将其实现出来。
4.配置中心地方
配置实时生效:
传统的静态配置方式要想修改某个配置只能修改之后重新发布应用,要实现动态性,可以选择使用数据库,通过定时轮询访问数据库来感知配置的变化。轮询频率低感知配置变化的延时就长,轮询频率高,感知配置变化的延时就短,但比较损耗性能,需要在实时性和性能之间做折中。配置中心专门针对这个业务场景,兼顾实时性和一致性来管理动态配置。
配置管理流程:
配置的权限管控、灰度发布、版本管理、格式检验和安全配置等一系列的配置管理相关的特性也是配置中心不可获取的一部分。
目前市面上用的比较多的配置中心有:(按开源时间排序)
Disconf
2014年7月百度开源的配置管理中心,同样具备配置的管理能力,不过目前已经不维护了,最近的一次提交是两年前了。
Spring Cloud Config
2014年9月开源,Spring Cloud 生态组件,可以和Spring Cloud体系无缝整合。
Apollo
2016年5月,携程开源的配置管理中心,具备规范的权限、流程治理等特性。
Nacos
2018年6月,阿里开源的配置中心,也可以做DNS和RPC的服务发现。
由于Disconf不再维护,下面对比一下Spring Cloud Config、Apollo和Nacos。
Spring Cloud Config、Apollo和Nacos在配置管理领域的概念基本相同,但是也存在一些不同的点,使用配置的过程中会涉及到一些比较重要的概念。
应用
应用是客户端系统的基本单位,Spring Cloud Config 将应用名称和对应Git中的文件名称关联起来了,这样可以起到多个应用配置相互隔离的作用。Apollo的配置都是在某个应用下面的(除了公共配置),也起到了多个应用配置相互隔离的作用。Nacos的应用概念比较弱,只有一个用于区分配置的额外属性,不过可以使用 Group 来做应用字段,可以起到隔离作用。
集群
不同的环境可以搭建不同的集群,这样可以起到物理隔离的作用,Spring Cloud Config、Apollo、Nacos都支持多个集群。
Label Profile & 环境 & 命名空间
Spring Cloud Config可以使用Label和Profile来做逻辑隔离,Label指远程仓库的分支,Profile类似Maven Profile可以区分环境,比如{application}-{profile}.properties。
Nacos的命名空间和Apollo的环境一样,是一个逻辑概念,可以作为环境逻辑隔离。Apollo中的命名空间指配置的名称,具体的配置项指配置文件中的一个Property。
作为配置中心,配置的整个管理流程应该具备流程化能力。
灰度发布
配置的灰度发布是配置中心比较重要的功能,当配置的变更影响比较大的时候,需要先在部分应用实例中验证配置的变更是否符合预期,然后再推送到所有应用实例。
Spring Cloud Config支持通过/bus/refresh端点的destination参数来指定要更新配置的机器,不过整个流程不够自动化和体系化。
Apollo可以直接在控制台上点灰度发布指定发布机器的IP,接着再全量发布,做得比较体系化。
Nacos目前发布到0.9版本,还不支持灰度发布。
权限管理
配置的变更和代码变更都是对应用运行逻辑的改变,重要的配置变更常常会带来核弹的效果,对于配置变更的权限管控和审计能力同样是配置中心重要的功能。
Spring Cloud Config依赖Git的权限管理能力,开源的GitHub权限控制可以分为Admin、Write和Read权限,权限管理比较完善。
Apollo通过项目的维度来对配置进行权限管理,一个项目的owner可以授权给其他用户配置的修改发布权限。
Nacos目前看还不具备权限管理能力。
版本管理&回滚
当配置变更不符合预期的时候,需要根据配置的发布版本进行回滚。Spring Cloud Config、Apollo和Nacos都具备配置的版本管理和回滚能力,可以在控制台上查看配置的变更情况或进行回滚操作。Spring Cloud Config通过Git来做版本管理,更方便些。
配置格式校验
应用的配置数据存储在配置中心一般都会以一种配置格式存储,比如Properties、Json、Yaml等,如果配置格式错误,会导致客户端解析配置失败引起生产故障,配置中心对配置的格式校验能够有效防止人为错误操作的发生,是配置中心核心功能中的刚需。
Spring Cloud Config使用Git,目前还不支持格式检验,格式的正确性依赖研发人员自己。
Apollo和Nacos都会对配置格式的正确性进行检验,可以有效防止人为错误。
监听查询
当排查问题或者进行统计的时候,需要知道一个配置被哪些应用实例使用到,以及一个实例使用到了哪些配置。
Spring Cloud Config使用Spring Cloud Bus推送配置变更,Spring Cloud Bus兼容 RabbitMQ、Kafka等,支持查询订阅Topic和Consumer的订阅关系。
Apollo可以通过灰度实例列表查看监听配置的实例列表,但实例监听的配置(Apollo称为命名空间)目前还没有展示出来。
Nacos可以查看监听配置的实例,也可以查看实例监听的配置情况。
基本上,这三个产品都具备监听查询能力,在我们自己的使用过程中,Nacos使用起来相对简单,易用性相对更好些。
多环境
在实际生产中,配置中心常常需要涉及多环境或者多集群,业务在开发的时候可以将开发环境和生产环境分开,或者根据不同的业务线存在多个生产环境。如果各个环境之间的相互影响比较小(开发环境影响到生产环境稳定性),配置中心可以通过逻辑隔离的方式支持多环境。
Spring Cloud Config支持Profile的方式隔离多个环境,通过在Git上配置多个Profile的配置文件,客户端启动时指定Profile就可以访问对应的配置文件。
Apollo也支持多环境,在控制台创建配置的时候就要指定配置所在的环境,客户端在启动的时候指定JVM参数ENV来访问对应环境的配置文件。
Nacos通过命名空间来支持多环境,每个命名空间的配置相互隔离,客户端指定想要访问的命名空间就可以达到逻辑隔离的作用。
多集群
当对稳定性要求比较高,不允许各个环境相互影响的时候,需要将多个环境通过多集群的方式进行物理隔离。
Spring Cloud Config可以通过搭建多套Config Server,Git使用同一个Git的多个仓库,来实现物理隔离。
Apollo可以搭建多套集群,Apollo的控制台和数据更新推送服务分开部署,控制台部署一套就可以管控多个集群。
Nacos控制台和后端配置服务是部署在一起的,可以通过不同的域名切换来支持多集群。
当配置变更的时候,配置中心需要将配置实时推送到应用客户端。
Nacos和Apollo配置推送都是基于HTTP长轮询,客户端和配置中心建立HTTP长联接,当配置变更的的时候,配置中心把配置推送到客户端。
Spring Cloud Config原生不支持配置的实时推送,需要依赖Git的WebHook、Spring Cloud Bus和客户端/bus/refresh端点:
基于Git的WebHook,配置变更触发server端refresh
Server端接收到请求并发送给Spring Cloud Bus
Spring Cloud Bus接到消息并通知给客户端
客户端接收到通知,请求Server端获取最新配置
5.API网关
1、API网关介绍
API网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。
API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。
2、融入架构
API网关负责服务请求路由、组合及协议转换。客户端的所有请求都首先经过API网关,然后由它将请求路由到合适的微服务。API网管经常会通过调用多个微服务并合并结果来处理一个请求。它可以在Web协议(如HTTP与WebSocket)与内部使用的非Web友好协议之间转换。
API网关还能为每个客户端提供一个定制的API。通常,它会向移动客户端暴露一个粗粒度的API。例如,考虑下产品详情的场景。API网关可以提供一个端点(/productdetails?productid=xxx),使移动客户端可以通过一个请求获取所有的产品详情。API网关通过调用各个服务(产品信息、推荐、评论等等)并合并结果来处理请求。
6.服务注册发现
客户端服务发现模式
当使用客户端服务发现的时候,客户端负责决定可用的服务实例的网络地址,以及围绕他们的负载均衡。客户端向服务注册表(service registry)发送一个请求,服务注册表是一个可用服务实例的数据库。客户端使用一个负载均衡算法,去选择一个可用的服务实例,来响应这个请求,下图展示了这种模式的架构:
服务实例的网络地址是动态分配的。而且,由于自动扩展,失败和更新,服务实例的配置也经常变化。这样一来,你的客户端代码需要一套更精细的服务发现机制。
有两种主要的服务发现模式:客户端服务发现(client-side discovery)和服务器端服务发现(server-side discovery)。我们首先来看下客户端服务发现。
当使用客户端服务发现的时候,客户端负责决定可用的服务实例的网络地址,以及围绕他们的负载均衡。客户端向服务注册表(service registry)发送一个请求,服务注册表是一个可用服务实例的数据库。客户端使用一个负载均衡算法,去选择一个可用的服务实例,来响应这个请求,下图展示了这种模式的架构:
一个服务实例被启动时,它的网络地址会被写到注册表上;当服务实例终止时,再从注册表中删除。这个服务实例的注册表通过心跳机制动态刷新。
Netflix OSS提供了一个客户端服务发现的好例子。Netflix Eureka是一个服务注册表,提供了REST API用来管理服务实例的注册和查询可用的实例。Netflix Ribbon是一个IPC客户端,和Eureka一起处理可用服务实例的负载均衡。下面会深入讨论Eureka。
客户端的服务发现模式有优势也有缺点。这种模式相对直接,但是除了服务注册表,没有其它动态的部分了。而且,由于客户端知道可用的服务实例,可以做到智能的,应用明确的负载均衡决策,比如一直用hash算法。这种模式的一个重大缺陷在于,客户端和服务注册表是一一对应的,必须为服务客户端用到的每一种编程语言和框架实现客户端服务发现逻辑。
服务器端服务发现模式
下图展示了这种模式的架构
客户端通过负载均衡器向一个服务发送请求,这个负载均衡器会查询服务注册表,并将请求路由到可用的服务实例上。通过客户端的服务发现,服务实例在服务注册表上被注册和注销。
AWS的ELB(Elastic Load Blancer)就是一个服务器端服务发现路由器。一个ELB通常被用来均衡来自互联网的外部流量,也可以用ELB去均衡流向VPC(Virtual Private Cloud)的流量。一个客户端通过ELB发送请求(HTTP或TCP)时,使用的是DNS,ELB会均衡这些注册的EC2实例或ECS(EC2 Container Service)容器的流量。没有另外的服务注册表,EC2实例和ECS容器也只会在ELB上注册。
HTTP服务器和类似Nginx、Nginx Plus的负载均衡器也可以被用做服务器端服务发现负载均衡器。例如,Consul Template可以用来动态配置Nginx的反向代理。
Consul Template定期从存储在Consul服务注册表的数据中,生成任意的配置文件。每当文件变化时,会运行一个shell命令。比如,Consul Template可以生成一个配置反向代理的nginx.conf文件,然后运行一个命令告诉Nginx去重新加载配置。还有一个更复杂的实现,通过HTTP API或DNS去动态地重新配置Nginx Plus。
有些部署环境,比如Kubernetes和Marathon会在集群中的每个host上运行一个代理。这个代理承担了服务器端服务发现负载均衡器的角色。为了向一个服务发送一个请求,一个客户端使用host的IP地址和服务分配的端口,通过代理路由这个请求。这个代理会直接将请求发送到集群上可用的服务实例。
服务器端服务发现模式也是优势和缺陷并存。最大的好处在于服务发现的细节被从客户端中抽象出来,客户端只需要向负载均衡器发送请求,不需要为服务客户端使用的每一种语言和框架,实现服务发现逻辑;另外,这种模式也有一些问题,除非这个负载均衡器是由部署环境提供的,又是另一个高需要启动和管理的可用的系统组件。
服务注册表(Service Registry)
服务注册表是服务发现的关键部分,是一个包含了服务实例的网络地址的数据库,必须是高可用和最新的。客户端可以缓存从服务注册表处获得的网络地址。但是,这些信息最终会失效,客户端会找不到服务实例。所以,服务注册表由一个服务器集群组成,通过应用协议来保持一致性。
正如上面提到的,Netflix Eureka是一个服务注册表的好例子。它提供了一个REST API用来注册和查询服务实例。一个服务实例通过POST请求来注册自己的网络位置,每隔30秒要通过一个PUT请求重新注册。注册表中的一个条目会因为一个HTTP DELETE请求或实例注册超时而被删除,客户端通过一个HTTP GET请求来检索注册的服务实例。
Netflix通过在每个EC2的可用区中,运行一个或多个Eureka服务器实现高可用。每个运行在EC2实例上的Eureka服务器都有一个弹性的IP地址。DNS TEXT records用来存储Eureka集群配置,实际上是从可用区到Eureka服务器网络地址的列表的映射。当一个Eureka服务器启动时,会向DNS发送请求,检索Eureka集群的配置,定位节点,并为自己分配一个未占用的弹性IP地址。
Eureka客户端(服务和服务客户端)查询DNS去寻找Eureka服务器的网络地址。客户端更想使用这个可用区内的Eureka服务器,如果没有可用的Eureka服务器,客户端会用另一个可用区内的Eureka服务器。
其它服务注册的例子包括:
一些系统,比如Kubernetes,Marathon和AWS没有一个明确的服务注册组件,这项功能是内置在基础设置中的。
下面我们来看看服务实例如何在注册表中注册。
服务注册(Service Registration)
前面提到了,服务实例必须要从注册表中注册和注销,有很多种方式来处理注册和注销的过程。一个选择是服务实例自己注册,即self-registration模式。另一种选择是其它的系统组件管理服务实例的注册,即第third-party registration模式。
自注册模式(The Self-Registration Pattern)
在self-registration模式中,服务实例负责从服务注册表中注册和注销。如果需要的话,一个服务实例发送心跳请求防止注册过期。下图展示了这种模式的架构:
Netflix OSS Eureka客户端是这种方式的一个好例子。Eureka客户端处理服务实例注册和注销的所有问题。Spring Cloud实现包括服务发现在内的多种模式,简化了Eureka的服务实例自动注册。仅仅通过@EnableEurekaClient注释就可以注释Java的配置类
self-registration模式同样也是优劣并存。优势之一在于简单,不需要其它组件。缺点是服务实例和服务注册表相对应,必须要为服务中用到的每种编程语言和框架实现注册代码。
第三方注册模式(The Third-Party Registration Pattern)
在third-party registration模式中,服务实例不会自己在服务注册表中注册,由另一个系统组件service registrar负责。service registrar通过轮询部署环境或订阅事件去跟踪运行中的实例的变化。当它注意到一个新的可用的服务实例时,就会到注册表中去注册。service registrar也会将停止的服务实例注销,下图展示了这种模式的架构。
service registrar的一个例子是开源的Registrator项目。它会自动注册和注销像Docker容器一样部署的服务。Registrator支持etcd和Consul等服务注册。
另一个service registrar的例子是NetflixOSS Prana。主要用于非JVM语言编写的服务,它是一个和服务实例配合的『双轮』应用。Prana会在Netflix Eureka上注册和注销实例。
service registrar是一个部署环境的内置组件,由Autoscaling Group创建的EC2实例可以被ELB自动注册。Kubernetes服务也可以自动注册。
third-party registration模式主要的优势在于解耦了服务和服务注册表。不需要为每个语言和框架都实现服务注册逻辑。服务实例注册由一个专用的服务集中实现。缺点是除了被内置到部署环境中,它本身也是一个高可用的系统组件,需要被启动和管理。
面试官心理分析
从这个问题开始,面试官就已经进入了实际的生产问题的面试了。
一个分布式系统中的某个接口,该如何保证幂等性?这个事儿其实是你做分布式系统的时候必须要考虑的一个生产环境的技术问题。啥意思呢?
你看,假如你有个服务提供一些接口供外部调用,这个服务部署在了 5 台机器上,接着有个接口就是付款接口。然后人家用户在前端上操作的时候,不知道为啥,总之就是一个订单不小心发起了两次支付请求,然后这俩请求分散在了这个服务部署的不同的机器上,好了,结果一个订单扣款扣两次。
或者是订单系统调用支付系统进行支付,结果不小心因为网络超时了,然后订单系统走了前面我们看到的那个重试机制,咔嚓给你重试了一把,好,支付系统收到一个支付请求两次,而且因为负载均衡算法落在了不同的机器上,尴尬了。。。
所以你肯定得知道这事儿,否则你做出来的分布式系统恐怕容易埋坑。
面试题剖析
这个不是技术问题,这个没有通用的一个方法,这个应该结合业务来保证幂等性。
所谓幂等性,就是说一个接口,多次发起同一个请求,你这个接口得保证结果是准确的,比如不能多扣款、不能多插入一条数据、不能将统计值多加了 1。这就是幂等性。
其实保证幂等性主要是三点:
实际运作过程中,你要结合自己的业务来,比如说利用 redis,用 orderId 作为唯一键。只有成功插入这个支付流水,才可以执行实际的支付扣款。
要求是支付一个订单,必须插入一条支付流水,order_id 建一个唯一键 unique key
。你在支付一个订单之前,先插入一条支付流水,order_id 就已经进去了。你就可以写一个标识到 redis 里面去,set order_id payed
,下一次重复请求过来了,先查 redis 的 order_id 对应的 value,如果是 payed
就说明已经支付过了,你就别重复支付了。
8.Nginx的用途
Nginx应该是现在最火的web和反向代理服务器,没有之一。她是一款诞生于俄罗斯的高性能web服务器,尤其在高并发情况下,相较Apache,有优异的表现。
那除了负载均衡,她还有什么其他的用途呢,下面我们来看下。
一、静态代理
Nginx擅长处理静态文件,是非常好的图片、文件服务器。把所有的静态资源的放到nginx上,可以使应用动静分离,性能更好。
二、负载均衡
Nginx通过反向代理可以实现服务的负载均衡,避免了服务器单节点故障,把请求按照一定的策略转发到不同的服务器上,达到负载的效果。
常用的负载均衡策略有:
1、轮询
将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端的每一台服务器,而不关心服务器实际的连接数和当前的系统负载。
2、加权轮询
不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同。
给配置高、负载低的机器配置更高的权重,让其处理更多的请;而配置低、负载高的机器,给其分配较低的权重,降低其系统负载,加权轮询能很好地处理这一问题,并将请求顺序且按照权重分配到后端。
3、ip_hash(源地址哈希法)
根据获取客户端的IP地址,通过哈希函数计算得到一个数值,用该数值对服务器列表的大小进行取模运算,得到的结果便是客户端要访问服务器的序号。
采用源地址哈希法进行负载均衡,同一IP地址的客户端,当后端服务器列表不变时,它每次都会映射到同一台后端服务器进行访问。
4、随机
通过系统的随机算法,根据后端服务器的列表大小值来随机选取其中的一台服务器进行访问。
5、least_conn(最小连接数法)
由于后端服务器的配置不尽相同,对于请求的处理有快有慢,最小连接数法根据后端服务器当前的连接情况,动态地选取其中当前积压连接数最少的一台服务器来处理当前的请求,尽可能地提高后端服务的利用效率,将负责合理地分流到每一台服务器。
三、限流
Nginx的限流模块,是基于漏桶算法实现的,在高并发的场景下非常实用,如下图:
1、配置参数
1)limit_req_zone定义在http块中,$binary_remote_addr 表示保存客户端IP地址的二进制形式。
2)Zone定义IP状态及URL访问频率的共享内存区域。
zone=keyword标识区域的名字,以及冒号后面跟区域大小。16000个IP地址的状态信息约1MB,所以示例中区域可以存储160000个IP地址。
3)Rate定义最大请求速率。示例中速率不能超过每秒100个请求。
2、设置限流
burst排队大小,nodelay不限制单个请求间的时间。
四、缓存
1、浏览器缓存,静态资源缓存用expire。
2、代理层缓存
五、黑白名单
1、不限流白名单
2、黑名单
大致来说,zookeeper 的使用场景如下,我就举几个简单的,大家能说几个就好了:
分布式协调
- 这个其实是 zookeeper 很经典的一个用法,简单来说,就好比,你 A 系统发送个请求到 mq,然后 B 系统消息消费之后处理了。那 A 系统
-
- 如何知道 B 系统的处理结果?用 zookeeper 就可以实现分布式系统之间的协调工作。A 系统发送请求之后可以在 zookeeper 上对某个节点
-
- 的值注册个监听器,一旦 B 系统处理完了就修改 zookeeper 那个节点的值,A 系统立马就可以收到通知,完美解决。
分布式锁
- 举个栗子。对某一个数据连续发出两个修改操作,两台机器同时收到了请求,但是只能一台机器先执行完另外一个机器再执行。那么此时就
-
- 可以使用 zookeeper 分布式锁,一个机器接收到了请求之后先获取 zookeeper 上的一把分布式锁,就是可以去创建一个 znode,接着执行
-
- 操作;然后另外一个机器也尝试去创建那个 znode,结果发现自己创建不了,因为被别人创建了,那只能等着,等第一个机器执行完了自己再执行。
元数据/配置信息管理
- zookeeper 可以用作很多系统的配置信息的管理,比如 kafka、storm 等等很多分布式系统都会选用 zookeeper 来做一些元数据、
-
- 配置信息的管理,包括 dubbo 注册中心不也支持 zookeeper 么?
HA高可用性
- 这个应该是很常见的,比如 hadoop、hdfs、yarn 等很多大数据系统,都选择基于 zookeeper 来开发 HA 高可用机制,就是一个
-
- 重要进程一般会做主备两个,主进程挂了立马通过 zookeeper 感知到切换到备用进程。
10.分布式
首先,需要了解系统为什么使用分布式。
随着互联网的发展,传统单工程项目的很多性能瓶颈越发凸显,性能瓶颈可以有几个方面:
1.应用服务层:随着用户量的增加,并发量增加,单项目难以承受如此大的并发请求导致的性能瓶颈。
2.底层数据库层:随着业务的发展,数据库压力越来越大,导致的性能瓶颈。
#场景1:应用系统集群的 Session 共享
应用系统集群最简单的就是服务器集群,比如:Tomcat 集群。应用系统集群的时候,比较凸显的问题是 Session 共享,Session 共享我们一是可以通过服务器插件来解决。另外一种也可以通过 Redis 等中间件实现。
#场景2:应用系统的服务化拆分
服务化拆分,是目前非常火热的一种方式。现在都在提微服务。通过对传统项目进行服务化拆分,达到服务独立解耦,单服务又可以横向扩容。服务化拆分遇到的经典问题就是分布式事务问题。目前,比较常用的分布式事务解决方案有几种:消息最终一致性、TCC 补偿型事务等。
#场景3:底层数据库的压力分摊
如果系统的性能压力出现在数据库,那我们就可以读写分离、分库分表等方案进行解决。
1服务器断电导致虚拟机数据丢失的恢复方法
这道题比较偏运维,并不是我们开发的职能范围。对于3年以内的开发算是超纲的面试题了,这种题目的回答最好说自己没有权限操作服务器,或者说是组长负责,技术经理负责。强行回答会陷进面试官的坑里
有兴趣可以看这篇文章:https://www.jb51.net/article/133246.htm
2 springboot和springcloud实现原理
Springboot:
个人对于springboot的理解是他对spring框架进行了模块化,将spring和目前使用比较多的技术框架集成配置打包,省了使用者在开发时自己去配置集成。大大简化了spring应用开发过程中的搭建和开发。同时更好的解决了各个框架集成时依赖包的版本冲突以及引用的不稳定性。
核心原理:
1> 基于SpringMVC无配置文件(纯Java)完全注解化+内置tomcat-embed-core实现SpringBoot框架,Main函数启动。
2> SpringBoot核心快速整合第三方框架原理:Maven继承依赖关系。
核心思想:开箱即用和约定优于配置
Springcloud:
springcloud虽然带有‘cloud’,但是它并不是云计算解决方案,而是在springboot基础上构建的,用于快速构建分布式系统的通用模式的工具集
特点:
1.约定由于配置
2.适用于各种环境。开发、部署在PC Server或各种云环境均可
3.隐藏了组件的复杂性,并提供声明式、无xml的配置方式
4.开箱即用,快速启动。
5.轻量级的组件。Springcloud 整合的组件大多比较轻量。
6.组件丰富,功能齐全。springcloud为微服务架构提供了非常完整的支持。例如配置管理、服务发现、断路器、微服务
7.选型中立、丰富
8.灵活。springcloud的组成部分是解耦的,开发人员可按需灵活挑选技术选型
对比:
SpringBoot专注于快速方便的开发单个个体微服务。
SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,为各个服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、精选决策、分布式会话等集成服务。
SpringBoot可以离开SpringCloud独立开发项目,但是SpringCloud离不开SpringBoot,属于依赖关系。
SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架。
现实中不大的项目使用springcloud只会徒增开发成本,所以在项目前期一般都是使用springboot,更有效率,快捷。
3 项目的构建流程
项目构建流程
项目立项
技术选型和项目框架搭建
需求分析
开发阶段
UI界面设计
编码开发
后端开发(整理完需求即可开始接口开发)
前端开发(依赖于UI界面设计和后端接口)
测试(完整的测试由测试==>修改==>回归测试组成)
功能测试
性能测试
上线
4 redis项目应用,在哪块,存哪些数据
redis的使用场景很多,回答是不但需要答出在哪里使用,最好要答出为什么这么使用
1) 登录,权限控制:session 数据的缓存
具体步骤:
用户进行登录时,提交的登录表单,放入request;
服务器端校验用户名和密码;
通过后将用户信息存储到Redis中,在数据库中的key为session_id;
服务器返回的response中的set-cookie字段包含该session_id,客户端收到后将其值存入浏览器中;
客户端之后的操作的request中都包含session_id,服务器收到后提取出并在Redis中拿到该session,完成业务操作;
使用Redis来实现session的共享和存储,必须要保证session_id,不会被轻易获取和破解,并设置合理的失效时间,对敏感操作必须再次校验用户。
2) 首页列表等数据的缓存
这类数据量大,查询条件比较复杂,性能消耗比较大。重复多次请求会导致数据库压力过大,降低查询效率,甚至宕机
3) 购物车/收藏等个人数据的缓存
这类数据的特点1是操作次数多,且更新快。redis可以快速的写入,移除和修改,很好的匹配这类数据的特性。
特点2是数据结构,其他缓存的数据结构单一,没有很好能符合这类数据的结构,redis的数据结构丰富,关键是它的hash数据类型很好的符合了这类数据结构的需求
比如,购物车场景,我们首先需要两个HASH来存储,第一个HASH是用户与购物车之间的关系,第二个HASH是购物车中的商品列表。
先通过userId获取到shoppingCartId,然后再通过shoppingCartId就可以获取到用户购物车的ProductIds。然后再通过ProductIds就可以异步读取用户购物车的商品信息。
总体来说redis适用于一些实时性要求不高但是请求次数较多的数据。
Redis的特点(必须了解):
5 如何解决高并发请求
高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。
高并发相关常用的一些指标:
响应时间:系统对请求做出响应的时间。例如系统处理一个HTTP请求需要200ms,这个200ms就是系统的响应时间。
吞吐量:单位时间内处理的请求数量。
QPS:每秒响应请求数。在互联网领域,这个指标和吞吐量区分的没有这么明显。
并发用户数:同时承载正常使用系统功能的用户数量。例如一个即时通讯系统,同时在线量一定程度上代表了系统的并发用户数。
互联网分布式架构设计,提高系统并发能力的方式,方法论上主要有两种:垂直扩展(Scale Up)与水平扩展(Scale Out)。
垂直扩展:提升单机处理能力。垂直扩展的方式又有两种:
(1)增强单机硬件性能,例如:增加CPU核数如32核,升级更好的网卡如万兆,升级更好的硬盘如SSD,扩充硬盘容量如2T,扩充系统内存如128G;
(2)提升单机架构性能,例如:使用Cache来减少IO次数,使用异步来增加单服务吞吐量,使用无锁数据结构来减少响应时间;
不管是提升单机硬件性能,还是提升单机架构性能,都有一个致命的不足:单机性能总是有极限的。所以互联网分布式架构设计高并发终极解决方案还是水平扩展。
水平扩展:只要增加服务器数量,就能线性扩充系统性能。水平扩展对系统架构设计是有要求的,如何在架构各层进行可水平扩展的设计,以及互联网公司架构各层常见的水平扩展实践
我们公司最早是一台服务器2核8Gb,当用户交互开始卡顿时升级到了4核16Gb,后面再次升级成了4核32GB。当用户量上来后,开始使用集群。
这里就是先做垂直扩展,后做水平扩展
项目初期为了快速迭代开发、推荐业务和成本看了,一般选用垂直扩展。当项目起来,用户量上来盈利了,会选用水平扩展来支撑日益复杂的需求
6 项目中那一块业务用到多线程了?
1 定时短信、站内信的发送,涉及上万用户,若单线程的话耗时会很长,多线程能大大缩短发送时间
2 定时操作列表记录(记录之间操作必须独立)
3 导入大批量数据
7 项目已经上线,出现bug怎么解决,如果宕机了怎么办
线上bug一般分为紧急和非紧急:
对于紧急的bug会立即组织测试和开发进行排查,若是代码问题实时修复,修复后测试没问题通知上线,上线后再测试和跟进
对于非紧急的bug会提交到bug管理工具(如禅道)上,统一时间进行排查和修复.修复测试没问题在指定时间进行上线
由于开发人员和测试人员有限,对于bug修复必须分情况和优先级进行修复,毕竟开发和测试有其他任务.
8 我们应该部署多少台服务器,每台服务器承受的压力是多少
部署多少台服务器根据项目实际情况而定,一般用户在10万以下并不会使用到集群,项目功能简单也不会使用到分布式服务.
公司项目初期为了快速迭代开发一般一个应用即可,这时部署一台服务器即可.从成本和效率上来说是最好的
项目中期的时候,随着功能复杂起来,会对功能模块进行拆分,这时部署会根据模块服务数和用户数来部署
目前我们公司的服务器情况:
商家数8000+,日活商家数1200+,用户数90w+,日活2w+:
PC web端2台服务器,H5 web端3台服务器,后台管理1台服务器,核心服务(处理各种定时任务)部署了一台服务器,上传服务器2台,短信服务器2台,支付服务器2台,图片服务器采用的是阿里云的OSS,数据库采用的是阿里云的RDS-mysql,缓存采用的是阿里云的redis
服务器承受的压力有很多因素:服务器的配置,应用的多少和性能都有关系,我们公司的服务器配置是8和16G,这种配置服务器能承受并发能过2000以上
9 实际开发中一个项目开发到什么样才能上线
正常上线必须通过产品和测试,产品需要确定需求中的功能是否已经实现,测试需要确定现有功能是否已经没有功能性问题,只有通过他们的验收项目或者新需求才能上线.
11 RabbitMQ能承载多少的高并发
RabbitMQ能承载多少高并发于服务器性能有关,测试4核8G的服务器上RabbitMQ的并发能破1000
这种题目只要说听测试同学说这个样子,毕竟不是所有人都会进行性能测试的.
12 数据库存储过程,项目中有用吗,处理什么业务
MySQL 5.0 版本开始支持存储过程。
存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。
存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。
存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。
存储过程就是具有名字的一段代码,用来完成一个特定的功能。
创建的存储过程保存在数据库的数据字典中。
优点
存储过程可封装,并隐藏复杂的商业逻辑。
存储过程可以回传值,并可以接受参数。
存储过程无法使用 SELECT 指令来运行,因为它是子程序,与查看表,数据表或用户定义函数不同。
存储过程可以用在数据检验,强制实行商业逻辑等。
缺点
存储过程,往往定制化于特定的数据库上,因为支持的编程语言不同。当切换到其他厂商的数据库系统时,需要重写原有的存储过程。
存储过程的性能调校与撰写,受限于各种数据库系统。
存储过程总的来说比较鸡肋:
1 受限于数据库和编程语言,导致各个公司使用时有顾虑,之后的迁移和转型成本会很大
2 编写要求不像普通sql方便,没有使用熟悉的编程语言开发效率高
目前我待过的几个项目中均没有使用到存储过程
13 MySQL的优化、百万千万级数据处理有什么方法
mysql优化:
目前用到过的:
1 实际开发中禁止使用’select *’
2 当知道结果只有一行数据时使用 LIMIT 1,这样一来,MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查少下一条符合记录的数据。
3 为常用的搜索字段建索引
4永远为每张表设置一个ID做为其主键,而且最好的是一个INT型的(推荐使用UNSIGNED),并设置上自动增加的AUTO_INCREMENT标志。
5 like中’%‘在前面用不到索引,尽可能将’%‘放在后面,进行有模糊查询
6 mysql 不支持函数转换,所以字段前面不能加函数,否则这将用不到索引
7 字段类型转换导致不用索引,如字符串类型的不用引号,数字类型的用引号等,这有可能会用不到索引导致全表扫描
8 当数据量大的时候需要分库分表
网上推荐的还有:
1 在Join表的时候使用相当类型的例,并将其索引:当两个表中Join的字段是被建过索引时,MySQL内部会启动为你优化Join的SQL语句的机制
2 千万不要 ORDER BY RAND(),这样使用只会让你的数据库的性能呈指数级的下降
3 使用 ENUM 而不是 VARCHAR
4 or 的查询尽量用 union 代替(Innodb)
5 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
6 应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描
7应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描
8 in 和 not in 也要慎用,否则会导致全表扫描
当然要分析语句是否要优化一定得会使用Explain关键字,从 PROCEDURE ANALYSE() 取得建议
千、百万级数据处理方法:
当数据达到千百万级别是,查询的效率是个很大的问题,这时写完sql语句都应该使用explain来查看下语句的查询信息,运行下语句看看使用的时间
当效率很低的时候就必须进行优化了:
1 从语句的性能进行优化 -- 以上的优化手段都可以用
2 从表结构进行优化,常见的有
A)对表字段拆分,比如商品表,商品的名称,主图,规格等不变的字段放到基础信息表中,商品的实时信息放到附属表中;
B) 对大表进行按时间拆分表,比如用户表,对指定时间内(60天)没有登录过的用户放到一张用户冻结表,其他的放在用户表,当冻结表中的用户重新登录后再移至用户表中
3 从硬件上进行升级
前面两种是减少语句的扫描量(查询量),后面一种是提高扫描(查询)的性能和效率
14 Oracle出现索引失效怎么办
1. 选用适合的Oracle优化器
2、重建索引:alter index 索引名 rebuild 【online】
3、强制索引:给该语句加上hint后,强制其使用指定索引
15 jvm参数调优
JVM 中最大堆大小有三方面限制:
a.相关操作系统的数据模型(32-bt还是64-bit)限制;
b.系统的可用虚拟内存限制;
c.系统的可用物理内存限制。
32位系统下,一般限制在1.5G~2G;64为操作系统对内存无限制。
常见配置汇总
堆设置
-Xms:初始堆大小
-Xmx:最大堆大小
-Xmn:年轻代大小
-Xss:每个线程的虚拟机栈大小
-XX:NewSize=n:设置年轻代大小
-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
-XX:MaxPermSize=n:设置持久代大小
-XX:MaxTenuringThreshold=n:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代
收集器设置
-XX:+UseSerialGC:设置串行收集器
-XX:+UseParallelGC:设置并行收集器
-XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等
-XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集
-XX:+UseParalledlOldGC:设置并行年老代收集器
-XX:+UseConcMarkSweepGC:设置并发收集器
-XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。
-XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。
-XX:+DisableExplicitGC,这个参数作用是禁止代码中显示调用GC
垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
-XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中断的执行时间
-XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间。
-XX:PrintHeapAtGC:打印GC前后的详细堆栈信息
-Xloggc:filename:与上面几个配合使用,把相关日志信息记录到文件以便分析。
并发收集器设置
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX:+UseParNewGC:设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。
-XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。
-XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片
调优总结
年轻代大小选择
响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。
吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。
年老代大小选择
响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:
并发垃圾收集信息
持久代并发收集次数
传统GC信息
花在年轻代和年老代回收上的时间比例
减少年轻代和年老代花费的时间,一般会提高应用的效率
吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。
较小堆引起的碎片问题
因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:
-XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。
-XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩
经典案例:
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
-Xmx3550m:设置JVM最大可用内存为3550M。
-Xms3550m:设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn2g:设置年轻代大小为2G。整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss128k:设置每个线程的虚拟机栈大小。JDK5.0以后每个线程栈大小为1M,以前每个线程栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
-XX:MaxPermSize=16m:设置持久代大小为16m。
-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
16 自旋锁、分布式锁
何谓自旋锁?它是为实现保护共享资源而提出一种锁机制。
其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。
自旋锁是一种比较低级的保护数据结构或代码片段的原始方式,这种锁可能存在两个问题:
死锁。试图递归地获得自旋锁必然会引起死锁:递归程序的持有实例在第二个实例循环,以试图获得相同自旋锁时,不会释放此自旋锁。在递归程序中使用自旋锁应遵守下列策略:递归程序决不能在持有自旋锁时调用它自己,也决不能在递归调用时试图获得相同的自旋锁。此外如果一个进程已经将资源锁定,那么,即使其它申请这个资源的进程不停地疯狂“自旋”,也无法获得资源,从而进入死循环。
过多占用cpu资源。如果不加限制,由于申请者一直在循环等待,因此自旋锁在锁定的时候,如果不成功,不会睡眠,会持续的尝试,单cpu的时候自旋锁会让其它process动不了. 因此,一般自旋锁实现会有一个参数限定最多持续尝试次数. 超出后, 自旋锁放弃当前time slice. 等下一次机会。
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。
目前一般会使用redis来实现分布式锁,核心代码:
17 单点登录原理
单点登录(Single Sign On),简称为 SSO。是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
有一个独立的认证中心,只有认证中心才能接受用户的用户名和密码等信息进行认证,其他系统不提供登录入口,只接受认证中心的间接授权。间接授权通过令牌实现,当用户提供的用户名和密码通过认证中心认证后,认证中心会创建授权令牌,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌即得到了授权,然后创建局部会话
特点: 一处登录,处处穿梭
18 整个系统从前端到数据库再到前端,代码优化,结构优化,通常考虑哪些问题
1 请求的耗时
2 请求的个数
3 查询的个数
4 查询的耗时
5 代码运行的耗时
6 代码的性能
7 代码的耦合性
8 代码的可读性
总之:优化一定得从以下几个方面考虑
性能:如代码的耗时,请求响应速度,请求的个数,查询的个数,查询的耗时
可维护性:如代码的耦合性,可读性,可维护性
19 怎么防止接口暴露别人用工具恶意刷接口
常见的方式:
1 图形验证码,这常用于短信接口的防护
2 限定请求次数,常见于秒杀,抢购等功能中
3 流程条件限制,如满足一定条件,注册用户,获取权限等
4 ip地址限定,java中一般写拦截器对统一ip进行判断,同一ip指定时间内只能访问指定次数
5 服务器接口验证:常见于一些需要付费的接口,这类接口需要内部的token或秘钥才能请求
20 脏数据是怎么回事?
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据(Dirty Data),依据脏数据所做的操作可能是不正确的。
扩展:不可重复读是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读
21 脏数据有哪些危害?
脏数据在涉及到历史数据,资金等重要数据几乎是致命的。
比如:在秒杀系统中,脏数据的产生可能会导致库存不足,商家就会亏损;在资金系统中,若有脏数据会导致整个系统统账错误。
22 怎么在线上查看出现的bug
上线的项目查看bug一般是通过日志系统。所以项目中必须有详细日志记录,在更新数据前后,重要操作节点都应该有详细的日志记录。
23 项目中异常是如何处理
项目中异常的处理无非是两种方式:
1 try catch 抓取异常
2 throw 抛出异常
方式简单,但是注意的细节却不少:
1. 能通过预检查方式规避的RuntimeException不应该通过catch方式处理,如IndexOutofBoundsException(角标越位异常),NullPointException(空指针异常)
2. 异常不能用来做流程控制或条件控制:异常的效率远低于比较判断方式
3. try catch时不能一股脑的吧代码全包含进去,这只会增加问题定位的难度,使得代码无法根据不同的异常做出处理,同时也是不负责任的表现
4. 捕捉异常的目的是处理他,若不处理就应该将异常抛给他的调用者。在展示给用户之前必须处理掉
5. try catch中若有事务代码,在catch完后一定注意是否需要rollback事务
6. finally代码块中对资源对象、流对象进行关闭时若有异常也要做try catch处理
7. 捕获异常与抛出的异常类型要完全匹配,或者捕获的异常是抛出异常的父类
8. finally代码块中不能有return:当有return时就不会执行try catch中的return
24 项目日志是如何处理的,怎么跟踪线上问题,日志存在哪里
目前线上的日志使用的是log4j,测试时会记录debug级别的信息,上线时记录info级别的信息。一般更新数据库数据前后需要打日志,敏感操作节点也需要打日志.这些日志都会写到日志文件中保存起来,一般的日志文件保存7到15天.
跟踪线上问题:
1 先确定问题是在哪个页面或者哪个功能中,然后查看功能对应的日志文件,看看是否有Error信息或者Exception信息
2 若没有异常信息说明很可能是代码逻辑的问题,查看功能日志点看看日志情况,一般是能定位问题点
3 若从日志中定位不出来只能是复盘功能代码
日志的存储:
单服务器上日志一般存在指定的目录下,可在日志配置中定义
分布式/集群:可以存在各种服务器上,也可以使用日志服务器统一管理日志文件
25 和测试如何交互的
测试的职责是找出项目应用中存在的问题,及时提交给开发修复.在上线前将问题尽可能的找出处理掉.现在很多开发觉得测试和他们是对立面,经常和测试针锋相对,推脱问题.其实测试和开发都是为了将项目功能尽可能的完善,交付给用户使用,这是他们的共同目的.
和测试的交互应该注重客观事实,根据测试提交的问题排查是什么原因造成的,然后有问题的解决掉,没问题的反馈给测试重新测试.重点是和测试多沟通,了解彼此的想法,不能造成误解,影响工作.
至少我现在公司开发和测试很和谐,每天测试工作时遇到需要紧急需马上解决的问题会马上和开发沟通,是否真正有问题由开发排查后反馈解决.非紧急的问题会提交到禅道上,由开发自己去看和解决自己的问题.测试每天定点看禅道的bug解决率,并提醒开发前去解决bug
27 BUG处理的过程是怎么样
问题的反馈: 测试测试出来或者用户反馈过来
问题的确定: 开发拿到问题后现在测试服中重现问题,无法重现的通过查看日志,复盘代码来定位问题
问题的解决: 一般小问题会在主干上修复并测试上线,大问题会开分支专门修复问题测试没问题后合并再测试上线
通知: 问题解决上线后通知相关的部门和人员修复结果(做到事事有结果)
26 项目如何部署
java项目部署目前一般部署在tomcat上,springboot出现后直接将项目打成jar包进运行。
部署前需要对环境进行确认:确认项目应用所需的环境是否正确,如系统环境,java版本,数据库版本,系统的硬件配置
对项目的配置文件进行确认:有些项目的配置文件在测试时需要修改,这时上线的时候需要改回去,如果没有check,可能会使用测试配置上线,这样就好导致上线出现问题
对数据库表字段进行确认:防止因为新增的表或字段未添加或名称有出入导致上线后报错
对上线时间的确认和通知
上线时对以前线上的代码进行备份:确保上线失败时可以回退
上线后:必须对所上线的功能进行再次测试,防止有问题
28 你们商城开发中都遇到过什bug
1 未做分布式锁时经常会出现并发和重复请求的数据:添加分布式锁
2 秒杀抢购被用户摸到接口,使用工具重复提交抢购:使用拦截器对同一ip同一接口进行请求间隔限制
3 一个用户多地登录导致数据异常:限制用户只能一地登录
4 数据库被清空:好在数据库数据每小时都有做备份,吧备份数据导回线上数据库,但还是损失了备份点之后的数据
5 系统达到瓶颈:升级硬件配置,使用集群
29 订单你们是怎么做的实现思路是什么
目前我碰到的有两种实现方式:
1 用户购买一件商品就生成一条订单记录,商品的库存减一
2 在商品发布时生成库存量个token,每次用户购买商品就获取一个token,同时生成一条订单记录,token使用完后失效
第一种需要注意重复提交和并发问题
第二种适用于商品库存量不大的应用
30 支付你们怎么做的,如何和支付宝和微信对接的,怎么保证安全
微信和支付宝支付接口的对接都有相应的流程,他们都提供了详细的对接demo.这两种支付方式以及快钱支付方式的对接都基本一致.
原理:
用户发起支付请求,应用服务端将支付的金额,支付的原因以及应用生成的唯一商户订单号使用支付接口方提供的加签方式进行加签后,生产预支付记录,同事调用支付接口进行支付.支付提交后有两种获取回执相应的方式:同步和异步.同步是用户点击支付完成,由服务端像支付接口方服务器发起查询,对查询结果进行校验操作.异步是支付接口方接受到支付打款后向我们应用指定接口发生支付成功的请求,我们对请求进行解析校验操作.无论异步还是同步都会返回我们生成的唯一商户订单号作为回执.操作成功后,根据商户订单进行更新记录
安全性: 这几种支付方式在提交支付是都会有加签步,当对应支付完成会有他们服务器回调我们接口.这时我们对他们的请求参数用指定的密钥和解签方式进行解签,对解签结果进行校验.
31 假设有用户在手机端和PC端同时对购物车里的商品进行下单处理怎么办
对购物车里的商品加锁,只有拿到锁的一方才能进行下单处理,另一方无法下单操作.
32 怎么保证用户登录之后,保证用户的数据一个安全性,保证用户的数据不会被第三方拦截
现在我们处理的方法:
1加密,一般是对称加密的方法.
2 信息使用特殊字符替换,如手机号:在服务端将手机号中间几位使用*替换掉
12.SVN
SVN是一个开放源代码的版本控制系统,通过采用分支管理系统的高效管理,简而言之就是用于多个人共同开发同一个项目,实现共享资源,实现最终集中式的管理。
基本操作
本地项目上传到SVN
项目名称 -> 右键 -> Team - > Share Project...
常用操作
commit
update
compare with ...
replace with ...
冲突处理
trunk、tags、branches 概述
Projects
|
---- trunk
| |
| ---- AnalyzeDataView
| ---- LogCollection
|
---- tags
| |
| ---- AnalyzeDataView_release_1.0
|
---- branches
|
---- AnalyzeDataView_bugfix_1_0
trunk : 用于主干开发
tags : 用于打标签,存档目录,不可修改
branches : 用于分支的开发,修复Bug,分支功能点开发等
另一种项目结构
ProjectA -> trunk -> ProjectA
-> tags
-> tag_release_1.0
-> tag_release_1.0.1
->branches
-> branch_bugfix_1.0
svn 常用命令
# 取指定日期的版本
svn co svn://192.168.0.2/project/nec/ebag/server/trunk/ProjectA/doc -r "{20160512}"
svn co https://211.149.198.196/svn/projects/AnalyzeData/projects/mapreduce-sdk-sys/shell --username XXX --password XXX
网址:https://www.cnblogs.com/leeke98/p/10250197.html
爬虫:
由于在我参与的那个项目中,出于市场营销的需要,为了获取最新的XXXX信息,我们用Python语言做了一个比较简单的网络爬虫,专门用来在新浪微博上爬取最近的热搜,给市场部的员工作为参考。
单点登录(SSO):
另外我们的项目因为分成了多个系统,为了解决cookie无法跨域导致的用户重复登录问题,我们使用了token技术来实现单点登录。
八级联动:
因为我参与的那个项目,在购物后填写收货地址时需要选择详细的地址,我们使用了下拉菜单的多级联动,也就是创建了多张表,每一张子表的副id都是父表的id,最后达到了八级联动。
调度:
为了解决项目中定时的公告发布与事务进行,我们使用了Quartz任务日程管理系统,比如准时发布全站公告和商城信息,以及在规定的时间运行某段代码。
14.shiro
在这个项目中,我们使用的是shiro安全框架管理用户登录以及资源权限的控制。
实现细节:用户在点击登录后,会将用户信息封装成一个token调用subject.login()方法提交认证,shiro的securityManager安全管理器会调用Authenticator执行认证,调用realm(realm类需要实现AuthorizingRealm接口重写认证和授权的方法)的token.getPrincipal()方法获取用户名以及密码,将密码与数据库中该用户的密码进行对比,如果验证成功,将返回 AuthenticationInfo 验证信息;此信息中包含了身份及凭证;如果验证失败将抛出相应的 AuthenticationException 实现。如果登录成功,会根据当前登录用户获取对应的菜单权限,在realm中进行用户的权限的授予。调用realm的hasRole判断当前用户是否有这个角色权限。isPermitted是shiro框架提供的判断是否拥有某个权限或所有权限,针对不同权限加载不同的菜单或者执行不同的操作权限。
15.ELK+Kafka
kafka:接收java程序投递的消息的日志队列
logstash:日志解析,格式化数据为json并输出到es中
elasticsearch:实时搜索搜索引擎,存储数据
kibana:基于es的数据可视化组件,查询数据
ELK+kafka日志系统原理(介质为日志)
Windows/linux的logstash(客户端)--->kafka(队列)--->kakfa上的logstash(也是一个客户端)--->ES(存储)--->kibana(界面)
核心在于logstash的配置文件
https://www.cnblogs.com/lingboweifu/p/11824167.html
16.WebService
在****项目中,网站需要显示各城市天气,提高用户体验。
项目组经过考虑,选择了通过第三方webservice服务来调用其他网站的天气服务。基于此我们查看服务说明,了解支持的服务接口。
在开发和测试阶段,我们.借助wsimport命令工具自动生成客户端代码。
WebService流程为,首先通过三级联动获得城市对应的ID,并导入所需要的依赖包。
当用户查询城市的请求信息到达系统时,系统将对应城市的信息通过第三方接口发送到webService,并的到处理结果
将处理结果通过ajax和fastjson将webService返回的json对象解析到前台并展示
JAVA中的反射是运行中的程序检查自己和软件运行环境的能力,它可以根据它发现的进行改变。通俗的讲就是反射可以在运行时根据指定的类名获得类的信息。
首先我们先明确两个概念,静态编译和动态编译。
静态编译:在编译时确定类型,绑定对象,即通过。 动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多 态的应用,有以降低类之间的藕合性。
我们可以明确的看出动态编译的好处,而反射就是运用了动态编译创建对象。
报表就是将项目中的信息以excel的形式导出来,共客户方财务或者我们的运维人员使用。
当然报表模块使用的也就是一个工具类Jcreporter,直接调用即可,像一般用于报表制作的还有poi。
mybatis分页:首先先在maven中导入jar包<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.4</version>
</dependency>
然后在spring.xml中配置
<!--配置sqlSessionFactoryBean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--配置别名-->
<property name="typeAliasesPackage" value="cn.entity"/>
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<value>
helperDialect=mysql
reasonable=true
</value>
</property>
</bean>
</array>
</property>
最后直接进行分页操作,创建一个返回值是pageinfo的接口
//分页查询
public PageInfo<HouseVo> selectByPage(Integer pageNum, Integer pageSize) {
//对下面的查询进行分页 PageHelper.startPage(pageNum,pageSize);
List<HouseVo> list = mapper.selectByPage();
return new PageInfo<HouseVo>(list,4);
}
1.搭建Elasticsearch环境并测试:
(1)删除es的容器
(2)删除es的镜像
(3)宿主机调内存: 执行命令:sudo sysctl -w vm.max_map_count=655360
(4)通过ftp软件修改docker-compose.yml中的 mem_limit: 2048M
(5)找到虚拟机, 执行命令:cd /home/px2/envdm/springcloudV2.0/
执行命令:docker-compose up -d elasticsearch
(6)测试es:http://192.168.115.158:9200 出现es的版本号说明配置环境成功。
2.搭建logstash
(1)打开物料中logstash
步骤:进入logstash的容器
vi /usr/local/logstash-6.3.0/config/logstash.yml(修改连接es的ip)
修改成:
http.host: "0.0.0.0"
xpack.monitoring.elasticsearch.url: http://192.168.115.158:9200
xpack.monitoring.elasticsearch.username: elastic
xpack.monitoring.elasticsearch.password: changeme
xpack.monitoring.enabled: false
(2)vi /usr/local/logstash-6.3.0/bin/logstash.conf
修改ip以及加入日志参数
input{
kafka{
bootstrap_servers => ["192.168.115.158:9092"]
group_id => "test-consumer-group"
auto_offset_reset => "latest"
consumer_threads => 5
decorate_events => true
topics => ["dm"]
type => "bhy"
}
}
output {
elasticsearch{
hosts=> ["192.168.115.158:9200"]
index=> "dmservice-%{+YYYY.MM.dd}"
}
stdout{
codec => json_lines
}
}
3.修改kibana配置文件
(1)找到elasticsearch.url:并修改成 "http://192.168.115.158:9200"
(2)访问:http://192.168.115.158:5601没有出现无es数据即说明成功。
4.k5afka
(1)找到server.properties
listeners=PLAINTEXT://0.0.0.0:9092
advertised.listeners=PLAINTEXT://192.168.115.158:9092
(2)启动kafka命令
./kafka-console-consumer.sh --bootstrap-server 127.0.0.1:9092 --topic dm --from-beginning
19.mycat是什么?你是怎么理解的?你们公司分库分表的分片规则是什么?搭建mycat环境常用的配置文件有哪些?
1、mycat是什么?
国内最活跃的、性能最好的开源数据库分库分表中间件
2、分片规则:取模
<tableRule name="mod-long">
<rule>
<columns>user_id</columns>
<algorithm>mod-long</algorithm>
</rule></tableRule><function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
<property name="count">3</property></function>
配置说明:
| 标签属性 | 说明 |
| --------- | -------------------- |
| columns | 标识将要分片的表字段 |
| algorithm | 分片函数 |
| count | 分片数量 |
根据 id 进行十进制求模预算。
3、搭建mycat环境常用的配置文件
rule.xml:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mycat:rule SYSTEM "rule.dtd"><mycat:rule xmlns:mycat="http://io.mycat/">
<tableRule name="auto-sharding-rang-mod-order">
<rule>
<columns>id</columns>
<algorithm>rang-mod-dm</algorithm>
</rule></tableRule><function name="rang-mod-dm" class="io.mycat.route.function.PartitionByMod">
<property name="count">3</property></function></mycat:rule>
schema.xml:
<?xml version="1.0"?><!DOCTYPE mycat:schema SYSTEM "schema.dtd"><mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="dm_order" checkSQLschema="true" sqlMaxLimit="100">
<table name="dm_order" primaryKey="id" autoIncrement="true" dataNode="dn1,dn2,dn3"
rule="auto-sharding-rang-mod-order"/>
</schema>
<dataNode name="dn1" dataHost="a" database="dm_order1"/><dataNode name="dn2" dataHost="a" database="dm_order2"/><dataNode name="dn3" dataHost="a" database="dm_order3"/><dataHost name="a" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql"
dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select 1</heartbeat>
<writeHost host="hostM1"
url="192.168.133.130:3306" user="root" password="123456">
</writeHost></dataHost></mycat:schema>
server.xml:
<?xml version="1.0" encoding="UTF-8"?><!-- - - Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License. - You
may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0
- - Unless required by applicable law or agreed to in writing, software -
distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the
License for the specific language governing permissions and - limitations
under the License. --><!DOCTYPE mycat:server SYSTEM "server.dtd"><mycat:server xmlns:mycat="http://io.mycat/">
<system>
<property name="useSqlStat">0</property> <!-- 1为开启实时统计、0为关闭 -->
<property name="useGlobleTableCheck">0</property> <!-- 1为开启全加班一致性检测、0为关闭 -->
<property name="sequnceHandlerType">2</property>
<!-- <property name="useCompression">1</property>--> <!--1为开启mysql压缩协议-->
<!-- <property name="fakeMySQLVersion">5.6.20</property>--> <!--设置模拟的MySQL版本号-->
<!-- <property name="processorBufferChunk">40960</property> -->
<!--
<property name="processors">1</property>
<property name="processorExecutor">32</property>
-->
<!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena-->
<property name="processorBufferPoolType">0</property>
<!--默认是65535 64K 用于sql解析时最大文本长度 -->
<!--<property name="maxStringLiteralLength">65535</property>-->
<!--<property name="sequnceHandlerType">0</property>-->
<!--<property name="backSocketNoDelay">1</property>-->
<!--<property name="frontSocketNoDelay">1</property>-->
<!--<property name="processorExecutor">16</property>-->
<!--
<property name="serverPort">8066</property> <property name="managerPort">9066</property>
<property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property>
<property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> -->
<!--分布式事务开关,0为不过滤分布式事务,1为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤),2为不过滤分布式事务,但是记录分布式事务日志-->
<property name="handleDistributedTransactions">0</property>
<!--
off heap for merge/order/group/limit 1开启 0关闭
-->
<property name="useOffHeapForMerge">1</property>
<!--
单位为m
-->
<property name="memoryPageSize">1m</property>
<!--
单位为k
-->
<property name="spillsFileBufferSize">1k</property>
<property name="useStreamOutput">0</property>
<!--
单位为m
-->
<property name="systemReserveMemorySize">384m</property>
<!--是否采用zookeeper协调切换 -->
<property name="useZKSwitch">true</property>
</system>
<!-- 全局SQL防火墙设置 -->
<!--
<firewall>
<whitehost>
<host host="127.0.0.1" user="mycat"/>
<host host="127.0.0.2" user="mycat"/>
</whitehost>
<blacklist check="false">
</blacklist>
</firewall>
-->
<user name="root">
<property name="password">123456</property>
<property name="schemas">dm_order</property>
<!-- 表级 DML 权限设置 -->
<!--
<privileges check="false">
<schema name="TESTDB" dml="0110" >
<table name="tb01" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges>
-->
</user>
</mycat:server>
20.SpringBoot 和 SpringCloud 之间关系?
SpringBoot:专注于快速方便的开发单个个体微服务(关注微观);
SpringCloud:关注全局的微服务协调治理框架,将SpringBoot开发的一个个单体微服务组合并管理起来(关注宏观);
SpringBoot可以离开SpringCloud独立使用,但是SpringCloud不可以离开SpringBoot,属于依赖关系。
21.权限管理
权限管理是系统的安全范畴,要求必须是合法的用户才可以访问系统(用户认证),且必须具有该资源的访问权限才可以访问该资源(授权)。
认证:对用户合法身份的校验,要求必须是合法的用户才可以访问系统。
授权:访问控制,必须具有该资源的访问权限才可以访问该资源。
权限模型:标准权限数据模型包括:用户、角色、权限(包括资源和权限)、用户角色关系、角色权限关系。
权限分配:通过UI界面方便给用户分配权限,对上边权限模型进行增、删、改、查操作。
权限控制:
基于角色的权限控制:根据角色判断是否有操作权限,因为角色的变化性较高,如果角色修改需要修改控制代码,系统可扩展性不强。
基于资源的权限控制:根据资源权限判断是否有操作权限,因为资源较为固定,如果角色修改或角色中权限修改不需要修改控制代码,使用此方法系统可维护性很强。建议使用。
权限管理的解决方案:
n 对于粗颗粒权限管理,建议在系统架构层面去解决,写系统架构级别统一代码(基础代码)。
粗颗粒权限:比如对系统的url、菜单、jsp页面、页面上按钮、类方法进行权限管理,即对资源类型进行权限管理。
n 对于细颗粒权限管理:
粗颗粒权限:比如用户id为001的用户信息(资源实例)、类型为t01的商品信息(资源实例),对资源实例进行权限管理,理解对数据级别的权限管理。
细颗粒权限管理是系统的业务逻辑,业务逻辑代码不方便抽取统一代码,建议在系统业务层进行处理。
基于url的权限管理(掌握):
企业开发常用的方法,使用web应用中filter来实现,用户请求url,通过filter拦截,判断用户身份是否合法(用户认证),判断请求的地址是否是用户权限范围内的url(授权)。
shiro:
shiro是一个权限管理框架,是apache下的开源项目。相比springsecurity框架更简单灵活,spring security对spring依赖较强。shiro可以实现web系统、c/s、分布式等系统权限管理。
shiro认证流程:(掌握)
1、subject(主体)请求认证,调用subject.login(token)
2、SecurityManager(安全管理器)执行认证
3、SecurityManager通过ModularRealmAuthenticator进行认证。
4、ModularRealmAuthenticator将token传给realm,realm根据token中用户信息从数据库查询用户信息(包括身份和凭证)
5、realm如果查询不到用户给ModularRealmAuthenticator返回null,ModularRealmAuthenticator抛出异常(用户不存在)
6、realm如果查询到用户给ModularRealmAuthenticator返回AuthenticationInfo(认证信息)
7、ModularRealmAuthenticator拿着AuthenticationInfo(认证信息)去进行凭证(密码 )比对。如果一致则认证通过,如果不致抛出异常(凭证错误)。
subject:主体
Authenticator:认证器( shiro提供)
realm(一般需要自定义):相当于数据源,认证器需要realm从数据源查询用户身份信息及权限信息。
CAS
会导致“ABA问题”。
CAS
算法实现一个重要前提需要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差类会导致数据的变化。
比如说一个线程 one 从内存位置 V 中取出 A,这时候另一个线程 two 也从内存中取出 A,并且 two 进行了一些操作变成了 B,然后 two 又将 V 位置的数据变成 A,这时候线程 one 进行 CAS 操作发现内存中仍然是 A,然后 one 操作成功。尽管线程 one 的 CAS 操作成功,但是不代表这个过程就是没有问题的。
部分乐观锁的实现是通过版本号(version)的方式来解决 ABA 问题,乐观锁每次在执行数据的修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行 +1 操作,否则就执行失败。因为每次操作的版本号都会随之增加,所以不会出现 ABA 问题,因为版本号只会增加不会减少。
CAS
是项乐观锁技术,当多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。
CAS
操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前值。)CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”这其实和乐观锁的冲突检查 + 数据更新的原理是一样的。
同步代码块是使用 monitorenter 和 monitorexit 指令实现的,同步方法(在这看不出来需要看 JVM 底层实现)依靠的是方法修饰符上的 ACC_SYNCHRONIZED 实现。
synchronized
和
lock
的用法区别
osynchronized(
隐式锁):在需要同步的对象中加入此控制,synchronized 可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。
olock
(显示锁):需要显示指定起始位置和终止位置。一般使用 ReentrantLock 类做为锁,多个线程中必须要使用一个 ReentrantLock 类做为对象才能保证锁的生效。且在加锁和解锁处需要通过
lock
()
和 unlock() 显示指出。所以一般会在
finally
块中写 unlock() 以防死锁。
synchronized
和
lock
性能区别 synchronized 是托管给 JVM 执行的,而
lock
是 Java 写的控制锁的代码。在 JDK 1.5 中,synchronize 是性能低效的。因为这是一个重量级操作,需要调用操作接口,导致有可能加锁消耗的系统时间比加锁以外的操作还多。相比之下使用 Java 提供的 Lock 对象,性能更高一些。但是到了 JDK 1.6,发生了变化。synchronize 在语义上很清晰,可以进行很多优化,有适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在 JDK 1.6 上 synchronize 的性能并不比 Lock 差。
synchronized
和
lock
机制区别
osynchronized
原始采用的是 CPU 悲观锁机制,即线程获得的是独占锁。独占锁意味着其 他线程只能依靠阻塞来等待线程释放锁。
oLock
用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁实现的机制就是 CAS 操作(Compare and Swap)。
在 JVM 底层
volatile
是采用“内存屏障”来实现的
缓存一致性协议(MESI协议)它确保每个缓存中使用的共享变量的副本是一致的。其核心思想如下:当某个 CPU 在写数据时,如果发现操作的变量是共享变量,则会通知其他 CPU 告知该变量的缓存行是无效的,因此其他 CPU 在读取该变量时,发现其无效会重新从主存中加载数据
乐观锁(Optimistic Lock):
每次获取数据的时候,都不会担心数据被修改,所以每次获取数据的时候都不会进行加锁,但是在更新数据的时候需要判断该数据是否被别人修改过。如果数据被其他线程修改,则不进行数据更新,如果数据没有被其他线程修改,则进行数据更新。由于数据没有进行加锁,期间该数据可以被其他线程进行读写操作。
比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量的查询操作,降低了系统的吞吐量。
线程安全是多线程领域的问题,线程安全可以简单理解为一个方法或者一个实例可以在多线程环境中使用而不会出现问题。
在 Java 多线程编程当中,提供了多种实现 Java 线程安全的方式:
最简单的方式,使用 Synchronization 关键字
使用 java.util.concurrent.atomic 包中的原子类,例如 AtomicInteger
使用 java.util.concurrent.locks 包中的锁
使用线程安全的集合 ConcurrentHashMap
使用
volatile
关键字,保证变量可见性(直接从内存读,而不是从线程 cache 读)
1.进程和线程的区别
一个程序下至少有一个进程,一个进程下至少有一个线程,一个进程下也可以有多个线程来增加程序的执行速度。
2.线程的生命周期,多线程并发会有什么影响
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。 <br>在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞 (Blocked)和死亡(Dead)
5
种状态。
多线程并发的影响:
多线程并发时会不安全,多线程同时操作对象的属性或者状态时,会因为线程之间的信息不同步,A线程读取到的状态已经过时,而A线程并不知道。
3.队列和栈的特性。在什么实际案例中见过?
堆栈的特性:先进后出,后进先出
队列的特性:先进先出,后进后出
4.synchro3ized的作用,java中还有那些常用的锁?
synchronized
它可以把任意一个非 NULL 的对象当作锁。他属于独占式的悲观锁,同时属于可重入锁。
1
.
作用于方法时,锁住的是对象的实例(
this
)
;
2
.
当作用于静态方法时,锁住的是Class实例, 会锁所有调用该方法的线程;
3
.
synchronized
作用于一个对象实例时,锁住的是所有以该对象为锁的代码块。
Java
中常见的锁还有乐观锁、悲观锁、自旋锁
1
.
volatile
本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;
synchronized
则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
2
.
volatile
仅能使用在变量级别;
synchronized
则可以使用在变量、方法、和类级别的
3
.
volatile
仅能实现变量的修改可见性,不能保证原子性;而
synchronized
则可以保证变量的修改可见性和原子性
4
.
volatile
不会造成线程的阻塞;
synchronized
可能会造成线程的阻塞。
5
.
volatile
标记的变量不会被编译器优化;
synchronized
标记的变量可以被编译器优化
6.spring的Bean的作用域和生命周期
Bean
的作用域:单例(singleton)、原型(prototype)、请求(request)、会话(session)、全局会话(global session)
Bean
的生命周期流程:
1
.
首先容器启动后,会对scope为singleton且非懒加载的bean进行实例化,
2
.
按照Bean定义信息配置信息,注入所有的属性,
3
.
如果Bean实现了BeanNameAware接口,会回调该接口的setBeanName()方法,传入该Bean的id,此时该Bean就获得了自己在配置文件中的id,
4
.
如果Bean实现了BeanFactoryAware接口,会回调该接口的setBeanFactory()方法,传入该Bean的BeanFactory,这样该Bean就获得了自己所在的BeanFactory,
5
.
如果Bean实现了ApplicationContextAware接口,会回调该接口的setApplicationContext()方法,传入该Bean的ApplicationContext,这样该Bean就获得了自己所在的ApplicationContext,
6
.
如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessBeforeInitialzation()方法,
7
.
如果Bean实现了InitializingBean接口,则会回调该接口的afterPropertiesSet()方法,
8
.
如果Bean配置了init-method方法,则会执行init-method配置的方法,
9
.
如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessAfterInitialization()方法,
10
.
经过流程
9
之后,就可以正式使用该Bean了,对于scope为singleton的Bean,Spring的ioc容器中会缓存一份该bean的实例,而对于scope为prototype的Bean,
每次被调用都会
new
一个新的对象,期生命周期就交给调用方管理了,不再是Spring容器进行管理了
11
.
容器关闭后,如果Bean实现了DisposableBean接口,则会回调该接口的destroy()方法,
12
.
如果Bean配置了destroy-method方法,则会执行destroy-method配置的方法,至此,整个Bean的生命周期结束。
4
7.spring常用的六个注解以及作用
@Component
(value)
配置类,当使用该注解时,SpringIOC会将这个类自动扫描成一个bean实例
不写的时候,默认是类名,且首字母小写
@Autowired
自动装配,会根据类型寻找指定的Bean,required:表示假如说在bean目录下找不到指定的注解还需不需要注入,默认是
true
修改为
false
时,表示假如说没有找到可以允许不注入。
@ImportResource
({
"classpath:xxx.xml"
})
能够读取多个xml文件,这样就可以引入多个xml定义的bean
@Service
标注于业务层组件上表示定义一个bean,自动根据所标注的组件名称实例化一个首字母为小写的bean。
@RequestBody
用来传对象参数
@Resource
用来自动装配Bean,激活一个命名资源的依赖注入。
@Resource
属性name可以定义被自动装配Bean的名称
事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。
也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。
事务的结束有两种,当事务中的所以步骤全部成功执行时,事务提交。如果其中一个步骤失败,将发生回滚操作,撤消撤消之前到事务开始时的所以操作。
并发问题可归纳为以下几类:
A.
丢失更新
B.
脏读
C.
不可重复读
D.
覆盖更新
E.
虚读(幻读)
事务的四种隔离性:
A.Serializable
(串行化):一个事务在执行过程中完全看不到其他事务对数据库所做的更新
(事务执行的时候不允许别的事务并发执行。事务串行化执行,事务只能一个接着一个地执行,而不能并发执行。)。
B.Repeatable Read
(可重复读):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,但是不能看到其他其他事务对已有记录的更新。
C.Read Commited
(读已提交数据):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,而且能看到其他事务已经提交的对已有记录的更新。
D.Read Uncommitted
(读未提交数据):一个事务在执行过程中可以看到其他事务没有提交的新插入的记录,而且能看到其他事务没有提交的对已有记录的更新。
9.什么是aop,spring的几种通知方式
aop
是面向切面编程,在整个系统中,动态横向添加新的功能还不改变原来的业务流程的编程
spring
的几种通知方式:
1
.
前置通知(方法调用前通知)
2
.
后置通知(方法调用后正常返回的时候通知,可以获取返回值,发生异常的时候不会执行)
3
.
最终通知(无论程序是否发生异常都会执行的)
4
.
异常通知(发生异常的执行)
5
.
环绕通知(方法执行的时候执行)
10.画出jvm的内存运行图并解释其作用
首先Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.
class
后缀),
然后由JVM中的类加载器加载各个类的字节码文件,加载完毕之后,交由JVM执行引擎执行。
在整个程序执行过程中,JVM会用一段空间来存储程序执行期间需要用到的数据和相关信息,
这段空间一般被称作为Runtime Data Area(运行时数据区),也就是我们常说的JVM内存。
因此,在Java中我们常常说到的内存管理就是针对这段空间进行管理
StreamApi
第一种排序:sorted(),自然排序
@Test
public
void
test8() {
List<String> list = Arrays.asList("aaa","bbb","ccc","ddd");
list.stream().sorted().forEach(System.out::println);
}
第二种排序:sorted(Comparator com)-定制排序
public
void
test8() {
List<String> list = Arrays.asList("qqq","aaa","bbb","ccc","ddd");
list.stream().sorted().forEach(System.out::println);
deptList.stream().sorted((x,y) -> {
if(x.getDeptno().equals(y.getDeptno())) {
return
x.getDname().compareTo(y.getDname());
}else
{
return
x.getLoc().compareTo(y.getLoc());
}
});
}
//
第三种按Map排序
//
创建一个Map,并填入数据
Map<String, Integer> codes =
new
HashMap<>();
codes.put(
"United States"
,
1
);
codes.put(
"Germany"
,
49
);
codes.put(
"France"
,
33
);
codes.put(
"China"
,
86
);
codes.put(
"Pakistan"
,
92
);
//
按照Map的键进行排序
Map<String, Integer> sortedMap = codes.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.collect(
Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldVal, newVal) -> oldVal,
LinkedHashMap::
new
)
);
//
将排序后的Map打印
sortedMap.entrySet().forEach(System.out::println);
//
按照value排序
Map<String, Integer> sortedMap2 = codes.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldVal, newVal) -> oldVal,
LinkedHashMap::
new
));
//
排序以后打印
sortedMap2.entrySet().forEach(System.out::println);
12.手写线程安全的单例模式
https:
//www.runoob.com/design-pattern/singleton-pattern.html
//
懒汉式,线程安全
public
class
Singleton {
private
static
Singleton instance;
private
Singleton (){}
public
static
synchronized
Singleton getInstance() {
if
(instance ==
null
) {
instance =
new
Singleton();
}
return
instance;
}
}
//
饿汉式
public
class
Singleton {
private
static
Singleton instance =
new
Singleton();
private
Singleton (){}
public
static
Singleton getInstance() {
return
instance;
}
}
//
双检锁/双重校验锁(DCL,即double-checked locking),JDK1.5起
public
class
Singleton {
private
volatile
static
Singleton singleton;
private
Singleton (){}
public
static
Singleton getSingleton() {
if
(singleton ==
null
) {
synchronized
(Singleton.
class
) {
if
(singleton ==
null
) {
singleton =
new
Singleton();
}
}
}
return
singleton;
}
}
//
登记式/静态内部类
public
class
Singleton {
private
static
class
SingletonHolder {
private
static
final
Singleton INSTANCE =
new
Singleton();
}
private
Singleton (){}
public
static
final
Singleton getInstance() {
return
SingletonHolder.INSTANCE;
}
}
//
枚举,JDK1.5起
public
enum
Singleton {
INSTANCE;
public
void
whateverMethod() {
}
}
经验之谈:一般情况下,不建议使用懒汉方式,建议使用饿汉方式。只有在要明确实现lazy loading 效果时,才会
使用登记方式。如果涉及到反序列化创建对象时,可以尝试使用枚举方式。如果有其他特殊的需求,可以考虑使用双检
锁方式。
13.设计一个秒杀的结构图
华为总部面试:
主要都是一些底层的东西,今天两个华为项目组,大概的题目是。
①基本数据类型和他们包装类之间(主要是有基本数据类型,为什么还要有包装类,基本数据类型和包装类在作为形参的时候有什么区别)
②jvm的内存模型【线程私有的线程共有的分别有哪些,分别存放什么,java8方法区改成了什么(改成了元数据区,又问他们有什么区别),什么又叫Java内存泄漏(怎么避免内存泄漏--主要是对象的引用类型)】,垃圾回收机制(新生代老年代的划分,他们分别用什么回收算法,常见的垃圾回收器,他们有什么特点,client和server模式下运行时他们的垃圾回收器是否相同(扯出来什么叫client,server模式,什么叫jit))。第二条很重要,两个项目组都问了
③hashmap 在1.7和1.8的底层实现有什么区别,这些改进有什么优点(以及引申出的桶排序(最快的排序,牺牲内存,保证性能),红黑树他的整体结构,四个规则)两个项目组都问了,第二个问的更深。
④spring通过bean创建对象时,怎么确保他的线程安全的
⑤由于我项目上写了websocket,http和https的区别(主要就是后者的加密,他的加密是基于什么的),当两个人之间通讯时,如何确保信息不会被伪造,篡改,截获(主要可逆加密中的对称加密和非对称加密,以及他们之间的配合使用),还有就是信任链当中的证书问题。
⑥抽象类和接口的区别
⑦你使用过哪些线程安全的类,为什么他们是线程安全的,如果给你一个线程不安全的类,你怎么将他改成线程安全的。
⑧构建一个大致的vue
⑨执行一个jar中的某个方法怎么执行。(主要是有一个MANIFEST.MF文件,在这里面配置main class.然后通过java -jar abc.jar)
⑩一台服务器近期cpu和内存突然使用率变高,怎么排查。
①说一下单点登录的具体实现
②我说我大学学过c和java,他问我java8有一个新特性叫lambda,他和c语言中的什么相似。
绿和:
1.几年工作经验?
2年
2.用的什么框架?
spring boot微服架构
3.是不是前后端分离
是前后端分离
4.用不用vue?用的话常用的指令有哪些?
用vue
v-if指令:是条件渲染指令,它根据表达式的真假来删除和插入元素
语法:v-if="expression"
v-show指令:显示内容 (同angular中的ng-show)
v-else指令:必须和v-if连用 不能单独使用 否则报错 模板编译错误
v-for指令:v-for指令基于一个数组渲染一个列表,它和JavaScript的遍历语法相似
语法:v-for="item in items"
v-bind指令: v-bind 指令可以在其名称后面带一个参数,中间放一个冒号隔开,这个参数通常是HTML元素的特性(attribute)
语法:v-bind:argument="expression"
v-model:v-model(表单元素设置了之后会忽略掉value,checked,selected),常用在表单 <input> 及 <textarea>,让表单元素和数据实现双向绑定(映射关系)
v-on指令:v-on 指令用于给监听DOM事件,它的用语法和v-bind是类似的,例如监听<a>元素的点击事件:<a v-on:click="doSomething">
v-text指令: v-text指令主要是防止页面首次加载时 {{}} 出现在页面上。将对象中数据变量值显示在绑定的标签内容上。
语法:v-text="expresstion"
5.js和jq熟悉吗
熟悉,两者区别如下1、本质上的区别
jQuery是一个JavaScript函数库。jQuery就是基于JavaScript语言写出来的一个框架,实质上还是JavaScript而已。
2、代码书写的不同
jQuery 极大地简化了 JavaScript 编程,jQuery代码比沉重的JavaScript轻灵多了,jquery设计的初衷就是要用更少的代码,漂亮的完成更多的功能。
3、在使用方法上的不同
(1)使用JavaScript和jQuery分别加载DOM:
JavaScript只会执行一次,而jQuery会执行多次。
(2)使用JavaScript和jQuery获取class方法:
JavaScript没有默认的获取class的方法 ,而JQuery, 可以通过$('.className') 调用class的方法。
6.springmvc工作流程
工作原理:1、 用户发送请求至前端控制器DispatcherServlet。
2、 DispatcherServlet收到请求调用HandlerMapping映射器。
3、 映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成对象及拦截器一并返回给DispatcherServlet。
4、 DispatcherServlet调用HandlerAdapter适配器。
5、 HandlerAdapter经过适配调用具体的处理器Controller(后端控制器)。
6、 Controller执行完成返回ModelAndView。
7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
9、 ViewReslover解析后返回具体View。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、 DispatcherServlet响应用户。
7.ArrayList和LinkedList的区别
共性:ArrayList与LinkedList都是List接口的实现类,因此都实现了List的所有未实现的方法,只是实现的方式有所不同。
区别:List接口的实现方式不同
ArrayList实现了List接口,以数组的方式来实现的,因此对于快速的随机取得对象的需求,使用ArrayList实现执行效率上会比较好。
LinkedList是采用链表的方式来实现List接口的,因此在进行insert和remove动作时效率要比ArrayList高。适合用来实现Stack(堆栈)与Queue(队列)。
8.redis支持的数据类型
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
9.线程问题
概念:一个程序可以有多条执行线索同时执行,一个线程就是程序中的一条执行线索,
独立运行的基本单位。
状态:
新建状态:执行new()方法
就绪状态:执行start()方法
运行状态:执行run()方法,获取CPU资源
阻塞状态:执行sleep(),让出CPU资源
死亡状态:可用stop()和destroy()强制终止
实现线程方式:继承Thread类,实现Runnable接口 ,实现Callable接口
join():线程插队
sleep()和wait()区别:
sleep()是Thread类的方法,让出cpu,cpu去执行其他线程,会自动恢复。
wait()是Object类的方法,让出同步锁给等待此锁的线程,不会自动恢复。
run()和start()区别:
run()是让该线程所关联的执行代码(一个线程必须管理具体的执行代码)。
start()是启动一个线程,使线程就绪状态。
最佳线程数目=((线程等待时间+线程CPU时间)/线程CPU时间)*CPU数目
(线程等待时间占比越高,需要越多线程,CPU时间占比越高,需要越少线程)
10.vue的路由
前端路由和后端路由:
后端路由:对于普通的网站,所有的超链接都是url地址,所有url都对应服务器上对应的资源
前端路由:对于单页面应用程序来说,主要通过url的hash(#)来实现不同页面的切换,同时hash还有一个特点HTTP请求中不会包含hash相关的内容,所以单页面程序中的页面跳转主要用hash实现
在单页面应用程序中这种通过hash来改变页面的方式称作前端路由区别于后端路由
11.为什么用elk+kafka
因为单纯的使用elk 日志收集,进行io读取,容易数据丢失,logstash太多了,扩展不好。非实时性。
12.mycat分了哪些表
1. 分库表
表示表的数据存放在多个实际的数据库中,按照一定的规则决定将某条数据存放在某一个数据库的表中;
添加数据时,根据分库规则决定添加到哪个实际数据库表中;
更新和删除数据时,首先定位到数据在哪个实际的数据库表中,然后再进行修改;
查询数据时,从映射的每个数据库中查询符合条件的记录,然后聚合以后返回结果;
存在分库规则,比如rule="auto-sharding-long"
2. 全局表
在每个数据库中都存放了该表的一个副本;
在添加和修改、删除数据时,在每个实际数据库表中都进行添加,操作完成后,每个数据库表的内容仍然相同;
在查询数据库表时,随机选择一个实际数据库表中的数据来返回;
用type="global"来标识。
3. 一对一映射表
一个mycat中的逻辑表映射到一个实际的数据库表,逻辑表与物理表之间是1对1映射的;
13.Rabbitmq怎么实现的
RabbitMQ使用以及原理解析
RabbitMQ是一个实现了AMQP(Advanced Message Queuing Protocol)高级消息队列协议的消息队列服务,用的是Erlang语言。
几个概念说明:
Broker:它提供一种传输服务,它的角色就是维护一条从生产者到消费者的路线,保证数据能按照指定的方式进行传输,
Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。
Queue:消息的载体,每个消息都会被投到一个或多个队列。
Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来.
Routing Key:路由关键字,exchange根据这个关键字进行消息投递。
vhost:虚拟主机,一个broker里可以有多个vhost,用作不同用户的权限分离。
Producer:消息生产者,就是投递消息的程序.
Consumer:消息消费者,就是接受消息的程序.
Channel:消息通道,在客户端的每个连接里,可建立多个channel.
易九:
介绍项目
找一个模块的业务逻辑讲一下
介绍一下中台架构
Es是什么
拜客科技:
Final
修饰类,类不能被继承
修饰变量,变量变成常量,只能被赋值一次,一般会与
public
static
共用
修饰方法,方法不能被重写
Final
修饰对象,该对象不能再指向其他引用,换句话,该对象不能再接受
new
对象的赋值情况
1.接口和抽象
(1)接口的特点:
接口的关键字:interface
a.所有的方法都是用public abstract 修饰的。(不写也默认有)
b.接口不能实例化
c.实现类必须实现接口的所有方法
d.实现类可以实现多个接口(java中的"多继承"的关系)
e.所有的变量都是使用public static final 修饰的(静态常量)。
(2)接口用的是什么协议
a.HTTP 超文本传输协议
b.HTTPS 安全超文本传输协议
c.FTP 文件传输协议( Xshell的文件拖拽)
d.TCP 网络控制协议
e.IP 互联网协议
f.UDP 用户数据协议
(3) java中抽象类的特点:
a.抽象类可以定义构造函数
b.抽象类可以声明和使用字段
c.如果一个类中有抽象方法,那么这个类一定是抽象类
d.抽象类中可以存在普通属性、方法、静态属性和静态方法
e.抽象类中不一定有抽象方法
f.抽象类中的抽象方法需要有子类实现,如果子类不实现,则子类也需要定义为抽象的
(4) 区别:
a.抽象类可以声明和使用字段;接口则不能,但是可以创建静态的final常量
b.抽象类可以定义构造函数,接口不能
c.抽象类中的方法可以是public、protected、private或者默认的package;接口的方法都是public(不管是否声明,接口都是公开的)
d.java接口中声明的变量默认都是final的,抽象类可以包含非final变量
e.抽象类可以在不提供接口方法实现的情况下实现接口
f.接口是绝对抽象的,不可以被实例化。抽象类也不可以被实例化,但是如果它包含main方法的话是可以被调用的
2.String能被继承吗,为什么
不能被继承,因为String类有final修饰符,而final修饰的类是不能被继承的。
3.流
1.按流向区分:输出流: OutputStream,Writer 基类
输入流: InputSteam,Reader 基类2.数据单元分:字节流: 字节输入流InputStream,字节输出流OutputStream
字符流: 字符输入流Reader,字符输出流Writer
4.throws和throw的区别
throws:用来声明一个方法可能产生的所有异常,不做任何处理而是将异常往上传,谁调用我我就抛给谁。
用在方法声明后面,跟的是异常类名
可以跟多个异常类名,用逗号隔开
表示抛出异常,由该方法的调用者来处理
throws表示出现异常的一种可能性,并不一定会发生这些异常throw:则是用来抛出一个具体的异常类型。
用在方法体内,跟的是异常对象名
只能抛出一个异常对象名
表示抛出异常,由方法体内的语句处理
throw则是抛出了异常,执行throw则一定抛出了某种异常
5.事务的实现
(1)编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。
(2)基于 TransactionProxyFactoryBean的声明式事务管理
(3)基于 @Transactional 的声明式事务管理
(4)基于Aspectj AOP配置事务
6.Mybatis延时原理
- 使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,
-
- 比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,
-
- 那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),
-
- 于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。
7.数据库优化,回答了之后,他会问他就要在where中对字段进行null判断怎么解决
数据库优化:
1、在表中建立索引,优先考虑where、group by使用到的字段。
2、尽量避免使用select *,返回无用的字段会降低查询效率。如下:
SELECT * FROM t
优化方式:使用具体的字段代替*,只返回使用到的字段。
3、尽量避免使用in 和not in,会导致数据库引擎放弃索引进行全表扫描。如下:
SELECT * FROM t WHERE id IN (2,3)
SELECT * FROM t1 WHERE username IN (SELECT username FROM t2)
优化方式:如果是连续数值,可以用between代替。如下:
SELECT * FROM t WHERE id BETWEEN 2 AND 3
如果是子查询,可以用exists代替。如下:
SELECT * FROM t1 WHERE EXISTS (SELECT * FROM t2 WHERE t1.username = t2.username)
4、尽量避免使用or,会导致数据库引擎放弃索引进行全表扫描。如下:
SELECT * FROM t WHERE id = 1 OR id = 3
优化方式:可以用union代替or。如下:
SELECT * FROM t WHERE id = 1
UNION
SELECT * FROM t WHERE id = 3
(PS:如果or两边的字段是同一个,如例子中这样。貌似两种方式效率差不多,即使union扫描的是索引,or扫描的是全表)
5、尽量避免在字段开头模糊查询,会导致数据库引擎放弃索引进行全表扫描。如下:
SELECT * FROM t WHERE username LIKE '%li%'
优化方式:尽量在字段后面使用模糊查询。如下:
SELECT * FROM t WHERE username LIKE 'li%'
6、尽量避免进行null值的判断,会导致数据库引擎放弃索引进行全表扫描。如下:------------------where中对字段进行null判断怎么解决?
SELECT * FROM t WHERE score IS NULL
优化方式:可以给字段添加默认值0,对0值进行判断。如下:
SELECT * FROM t WHERE score = 0
7、尽量避免在where条件中等号的左侧进行表达式、函数操作,会导致数据库引擎放弃索引进行全表扫描。如下:
SELECT * FROM t2 WHERE score/10 = 9
SELECT * FROM t2 WHERE SUBSTR(username,1,2) = 'li'
优化方式:可以将表达式、函数操作移动到等号右侧。如下:
SELECT * FROM t2 WHERE score = 10*9
SELECT * FROM t2 WHERE username LIKE 'li%'
8、当数据量大时,避免使用where 1=1的条件。通常为了方便拼装查询条件,我们会默认使用该条件,数据库引擎会放弃索引进行全表扫描。如下:
SELECT * FROM t WHERE 1=1
优化方式:用代码拼装sql时进行判断,没where加where,有where加and。
8.Dubbo功能
9.Dubbo怎么部署,几种方式
- 方式:
-
- 1、 使用web容器(Tomcat、Jetty等)启动dubbo服务 :
增加端口管理复杂性, tomcat/jetty等都需要占用端口,dubbo服务也需要端口;浪费资源(内存),单独启动 tomcat,jetty占用内存大
运用:需要提供web服务的模块,一般dubbo client可以使用这种方式部署
2、使用自建Main方法类运行spring容器启动dubbo服务:Dobbo提供的优雅停机高级特性没用上,并且自已编写启动类可能会有缺陷
运用:在开发阶段测试dubbo server可以使用这种方式
3、使用Dubbo框架提供的Main方法类运行Spring容器启动服务:官方建议使用,dubbo框架本身提供启动类(com.alibaba.dubbo.container.Main),可实现优雅关机,打包的时候使用maven打成普通的jar包就可以了。
运用:生产上dubbo server可以用这种方式部署。
————————————————
版权声明:本文为CSDN博主「HsiunKao」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/growdane/article/details/84639182
10.Redis支持的数据类型,在什么情况下用
11.Reids的持久化,怎么实现
12.并行与并发的区别
13.什么是死锁
14.怎么防止死锁
15.同步关键字的实现原理
16.Springboot的注解,和里面包含的注解以及作用
17.Sleep和wait的区别
18.#{}和${}区别
19.Struts
20.自己设计一个shiro流程
一些笔试题:
1.有n个整数,使其前面各数顺序向后移m个位置,最后m个数变成最前面的m个数
import java.util.Scanner;public class lianxi36 {public static void main(String[] args) {
int N =10;
int[] a = new int[N];
Scanner s = new Scanner(System.in);
System.out.println("请输入10个整数:");
for(int i=0; i<N; i++) {
a[i] = s.nextInt();
}
System.out.print("你输入的数组为:");
for(int i=0; i<N; i++) {
System.out.print(a[i] + " ");
}
System.out.print("\n请输入向后移动的位数:");
int m = s.nextInt();
int[] b = new int[m];
for(int i=0; i<m; i++) {
b[i] = a[N-m+i];
}
for(int i=N-1; i>=m; i--) {
a[i] = a[i-m];
}
for(int i=0; i<m; i++) {
a[i] = b[i];
}
System.out.print("位移后的数组是:");
for(int i=0; i<N; i++) {
System.out.print(a[i] + " ");
}
}
}
2.给定一个整数N,按以下模式打印2*N行。如果N=4则
1
2*3
4*5*6
7*8*9*10
7*8*9*10
4*5*6
2*3
1
输入:n—>表示行数的整数
输出:按指定行数打印出该模式
public class printmatrix {
public static int helper[] = new int[100];
public static void calc(int n){
if(n==1){
helper[n]=1;
return;
}
if(helper[n-1]==0)
calc(n-1);
helper[n] = helper[n-1]+n;
}
public static void printNum(int n){
if(helper[n]==0){
calc(n);
}
String[] strs = new String[n+1];
int row = 1;
for(;row<=n;row++){
String str = new String();
for(int i=helper[row-1]+1;i<=helper[row];i++){
if(!str.equals(""))str+='*';
str += i;
}
strs[row] = str;
}
for(String str : strs){
if(str==null)continue;
System.out.println(str);
}
for(int i=n;i>0;i--){
System.out.println(strs[i]);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
printmatrix.printNum(4);
}
}
3.谈谈你对webService的看法
WebService,顾名思义就是基于Web的服务。它使用Web(HTTP)方式,接收和响应外部系统的某种请求。从而实现远程调用
4.谈谈你对存储过程的看法
我理解的存储过程就是预先定义好的SQL语句,然后存储起来,等你用的时候再把自己所需要的匹配的SQL语句用Execute(即exec)调用就行!
5.已知表(Sales)
1>写出求每个Company的Amout的合计在10000以上的数据的SQL语句。
2>过滤掉Company、Amout重复的数据。只保留一条。以上述数据为例。执行结果应该为:
6.如下代码有错么?错在哪里?请说明情况。
List list=null;
System.out.println(list.length);
null没有 .size()
你可以 List<object> aa= new List<object>(); (C#语言,java类似)
就是意思是new一个list对象 这样.size()就可以用了
7.谈谈你对java集合类的看法。List,Set,Map是否继承自Collection接口?
Collection是最基本的集合接口,声明了适用于JAVA集合(只包括Set和List)的通用方法。
Set 和List 都继承了Conllection;Set具有与Collection完全一样的接口,因此没有任何额外的功能,不像前面有两个不同的List。
实际上Set就是Collection,只 是行为不同。(这是继承与多态思想的典型应用:表现不同的行为。)Set不保存重复的元素(至于如何判断元素相同则较为负责)
Map没有继承于Collection接口 从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。
8.abstract class 和 interface有什么区别?
1. 抽象类可以有构造方法,接口不能有构造方法 2. 抽象类中可以有普通成员变量,接口中没有普通成员变量 3. 抽象类中可以包含非抽象的普通方法,接口中的所有方法必须是抽象的,不能有非抽象的普通方法 4. 抽象类中抽象方法的访问类型可以是public,protected和默认类型虽然(eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型,并且默认即为public abstract类型 5. 抽象类中可以包含静态方法,接口中不能包含静态方法 6. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型 7. 一个类可以实现多个接口,但只能继承一个抽象类
9.启动一个线程使用run()还是start()?
启动线程肯定要用start()方法。当用start()开始一个线程后,线程就进入就绪状态,使线程所代表的虚拟处理机处于可运行状态,
这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。当cpu分配给它时间时,才开始执行run()方法(如果有的话)。
start()是方法,它调用run()方法.而run()方法是你必须重写的. run()方法中包含的是线程的主体。
10.应用服务器有哪些?
根据确定文档内容的时间,所有文档可以划分为静态Web文档、动态web文档和活动文档。常用的Web应用服务器有:nginx、Apache、Tomcat、WebLogic、Resin、Glassfish、JBoss、WebSphere等等。
11.如何理解transaction事务的概念?
事务(transaction)的概念与特性
事务是一系列操作,这些操作组成了一个逻辑工作单元。这个逻辑工作
单元中的操作作为一个整体,要么全部成功,要么全部失败。
事务的特性(ACID):
1.原子性(Atomicity):
事务中的一系列操作作为一个原子性的逻辑工作单元,这个逻辑工作
单元中的操作要么全部成功,要么全部失败。
2.一致性(Consistency):
事务操作之前与操作之后数据库系统的数据状态是从一种一致性状态
转换到另一种一致性状态。
3.隔离性(Isolation)
多个事务并发执行时,互相不能看到对方的操作过程。
4.持久性(Durability)
一旦事务执行成功,对数据库系统的影响是持久性的。
12.String时最基本的数据类型吗?String和StringBuffer的区别?
String不是基本的数据类型
(1)String:是对象不是原始类型.为不可变对象,一旦被创建,就不能修改它的值.对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去.String 是final类,即不能被继承
(2)StringBuffer:是一个可变对象,当对他进行修改的时候不会像String那样重新建立对象。它只能通过构造函数来建立对象被建立以后,在内存中就会分配内存空间,并初始保存一个null.
向StringBuffer中付值的时候可以通过它的append方法.
13.int 和 Integer有什么区别?
1、Integer是int的包装类,int则是java的一种基本数据类型 2、Integer变量必须实例化后才能使用,而int变量不需要 3、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值 4、Integer的默认值是null,int的默认值是0
14.运行时异常与一般异常有何异同?
异常表示程序运行过程中可能出现的非正常状态,
运行时异常:表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,
但是并不要求必须声明抛出未被捕获的运行时异常。
一般异常:java编译器强制要求我们必需对出现的异常进行try…catch…处理的否则程序不能通过编译
15.说出Servlet的生命周期,并说出Servlet和CGI的区别?
Servlet的生命周期分为5个阶段:
实例化:Servlet容器创建Servlet类的实例。
初始化:该容器调用init()方法,通常会申请资源。
服务:由容器调用service()方法,(也就是doGet()和doPost())。
破坏:在释放Servlet实例之前调用destroy()方法,通常会释放资源。
不可用:释放内存的实例。
CGI(Common Gateway Interface通用网关接口)程序来实现数据在Web上的传输,使用的是如Perl这样的语言编写的,它对于客户端作出的每个请求,
必须创建CGI程序的一个新实例,这样占用大量的内存资源。由此才引入了Servlet技术。
Servlet是一个用java编写的应用程序,在服务器上运行,处理请求信息并将其发送到客户端。对于客户端的请求,只需要创建Servlet的实例一次,因此节省了大量的内存资源。
Servlet在初始化后就保留在内存中,因此每次作出请求时无需加载。
16.说出ArrayList,Vector,LinkedList的存储性能和特性
ArrayList和Vector都是使用数组方式存储数据,此 数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,
但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据 慢,Vector由于使用了synchronized方法(线程安全),通常性能上较ArrayList差,而LinkedList使用双向链表实现存储,
按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。
一.同步性:Vector是线程安全的,也就是说是同步的,而ArrayList是线程序不安全的,不是同步的
二.数据增长:当需要增长时,Vector 默认增长为原来一培,而ArrayList却是原来的一半
17.条件:
学生表student:学号sno,姓名name
课程成绩表student_score:学号sno,学科stype,分数score
(1)查询所有同学的学生编号,学生姓名,选课总数,所有课程的总成绩
(2)检索“01”课程分数小于60,按分数降序排列的学生信息
(3)查询至少有一门课与学号为"01"的同学所学相同的同学的信息
编写新增学生信息的rest接口,包括用到的注解
(1).controller
(2).service
(3).dao
(4).xml,sql层
------------题目------------
英斯特笔试:
1.进程和线程的区别
进程:是系统进行分配和管理资源的基本单位
线程:进程的一个执行单元, 是进程内调度的实体、是CPU调度和分派的基本单位, 是比进程更小的独立运行的基本单位。线程也被称为轻量级进程, 线程是程序执行的最小单位。
一个程序至少一个进程, 一个进程至少一个线程。
进程有自己的独立地址空间, 每启动一个进程, 系统就会为它分配地址空间, 建立数据表来维护代码段、堆栈段和数据段, 这种操作非常昂贵。
而线程是共享进程中的数据的, 使用相同的地址空间, 因此CPU切换一个线程的花费远比进程要小很多, 同时创建一个线程的开销啊也比进程要小很多。
线程之间的通信更方便, 同一进程下的线程共享全局变量、静态变量等数据, 而进程之间的通信需要以通信的方式进行。
如何处理好同步与互斥是编写多线程程序的难点。
多进程程序更健壮, 进程有独立的地址空间, 一个进程崩溃后, 在保护模式下不会对其它进程产生影响,
而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量, 但线程之间没有独立的空间, 所以可能一个线程出现问题, 进而导致整个程序出现问题
2.线程的生命周期,多线程并发会有什么影响
3.队列和栈的特性。在什么实际案例中见过?
4.synchroized的作用,java中还有那些常用的锁?
5.synchronized 和 volatile 的区别是什么?
6.spring的Bean的作用域和生命周期
7.spring常用的六个注解以及作用
8.解释什么是事务,多线程事务并发会产生哪些问题,简述事务的四种隔离性
9.什么是aop,spring的几种通知方式
10.画出jvm的内存运行图并解释其作用
11.手写快速排序算法
12.手写线程安全的单例模式
13.设计一个秒杀的结构图
华为总部面试:
主要都是一些底层的东西,今天两个华为项目组,大概的题目是。
①基本数据类型和他们包装类之间(主要是有基本数据类型,为什么还要有包装类,基本数据类型和包装类在作为形参的时候有什么区别)
②jvm的内存模型【线程私有的线程共有的分别有哪些,分别存放什么,java8方法区改成了什么(改成了元数据区,又问他们有什么区别),什么又叫Java内存泄漏(怎么避免内存泄漏--主要是对象的引用类型)】,垃圾回收机制(新生代老年代的划分,他们分别用什么回收算法,常见的垃圾回收器,他们有什么特点,client和server模式下运行时他们的垃圾回收器是否相同(扯出来什么叫client,server模式,什么叫jit))。第二条很重要,两个项目组都问了
③hashmap 在1.7和1.8的底层实现有什么区别,这些改进有什么优点(以及引申出的桶排序(最快的排序,牺牲内存,保证性能),红黑树他的整体结构,四个规则)两个项目组都问了,第二个问的更深。
④spring通过bean创建对象时,怎么确保他的线程安全的
⑤由于我项目上写了websocket,http和https的区别(主要就是后者的加密,他的加密是基于什么的),当两个人之间通讯时,如何确保信息不会被伪造,篡改,截获(主要可逆加密中的对称加密和非对称加密,以及他们之间的配合使用),还有就是信任链当中的证书问题。
⑥抽象类和接口的区别
⑦你使用过哪些线程安全的类,为什么他们是线程安全的,如果给你一个线程不安全的类,你怎么将他改成线程安全的。
⑧构建一个大致的vue
⑨执行一个jar中的某个方法怎么执行。(主要是有一个MANIFEST.MF文件,在这里面配置main class.然后通过java -jar abc.jar)
⑩一台服务器近期cpu和内存突然使用率变高,怎么排查。
①说一下单点登录的具体实现
②我说我大学学过c和java,他问我java8有一个新特性叫lambda,他和c语言中的什么相似。
绿和:
1.几年工作经验?
2.用的什么框架?
3.是不是前后端分离
4.用不用vue?用的话常用的指令有哪些?
5.js和jq熟悉吗
6.springmvc工作流程
7.ArrayList和LinkedList的区别
8.redis支持的数据类型
9.线程问题
10.介绍一下项目
11.vue的路由
12.为什么用elk+kafka
13.mycat分了哪些表
14.Rabbitmq怎么实现的
易九:
介绍项目
找一个模块的业务逻辑讲一下
介绍一下中台架构
Es是什么
拜客科技:
Final
1.接口和抽象
2.String能被继承吗,为什么
3.流
4.异常和throw的区别
5.事务的实现
6.Mybatis延时原理
7.数据库优化,回答了之后,他会问他就要在where中对字段进行null判断怎么解决
8.Dubbo功能
9.Dubbo怎么部署,几种方式
10.Redis支持的数据类型,在什么情况下用
11.Reids的持久化,怎么实现
12.并行与并发的区别
13.什么是死锁
14.怎么防止死锁
15.同步关键字的实现原理
16.Springboot的注解,和里面包含的注解以及作用
17.Sleep和wait的区别
18.#{}和${}区别
19.Struts
20.自己设计一个shiro流程
一些笔试题:
1.有n个整数,使其前面各数顺序向后移m个位置,最后m个数变成最前面的m个数
2.给定一个整数N,按以下模式打印2*N行。如果N=4则
1
2*3
4*5*6
7*8*9*10
7*8*9*10
4*5*6
2*3
1
输入:n—>表示行数的整数
输出:按指定行数打印出该模式
3.谈谈你对webService的看法
4.谈谈你对存储过程的看法
5.已知表(Sales)
1>写出求每个Company的Amout的合计在10000以上的数据的SQL语句。
2>过滤掉Company、Amout重复的数据。只保留一条。以上述数据为例。执行结果应该为:
6.如下代码有错么?错在哪里?请说明情况。
List list=null;
System.out.println(list.length);
7.谈谈你对java集合类的看法。List,Set,Map是否继承自Collection接口?
8.abstract class 和 interface有什么区别?
9.启动一个线程使用run()还是start()?
10.应用服务器有哪些?
11.如何理解transaction事务的概念?
12.String时最基本的数据类型吗?String和StringBuffer的区别?
13.int 和 Integer有什么区别?
14.运行时异常与一般异常有何异同?
15.说出Servlet的生命周期,并说出Servlet和CGI的区别?
16.说出ArrayList,Vector,LinkedList的存储性能和特性
17.条件:
学生表student:学号sno,姓名name
课程成绩表student_score:学号sno,学科stype,分数score
(1)查询所有同学的学生编号,学生姓名,选课总数,所有课程的总成绩
(2)检索“01”课程分数小于60,按分数降序排列的学生信息
(3)查询至少有一门课与学号为"01"的同学所学相同的同学的信息
编写新增学生信息的rest接口,包括用到的注解
(1).controller
(2).service
(3).dao
(4).xml,sql层
1.JDBC和hibernate的区别
(1)hibernate和jdbc主要区别就是,hibernate先检索缓存中的映射对象( 即hibernate操作的是对象),而jdbc则是直接操作数据库.
(2)Hibernate是JDBC的轻量级的对象封装,它是一个独立的对象持久层框架,和App Server,和EJB没有什么必然的联系。Hibernate可以用在任何JDBC可以使用的场合
(3)Hibernate是一个和JDBC密切关联的框架,所以Hibernate的兼容性和JDBC驱动,和数据库都有一定的关系,但是和使用它的Java程序,和App Server没有任何关系,也不存在兼容性问题。
还有一点,正确的使用JDBC技术,它的效率一定比hibernate要好,因为hibernate是基于jdbc的技术.
2.Echarts的实现原理
Canvas
基于像素
单个html,类似于画笔在画布上画画
Echarts基于canvas画图
Svg
基于对象模型
多个图形元素
高保真
3.ajax是如何进行前后台交互的
1、url 请求地址2、type 请求方式,默认是'GET',常用的还有'POST'3、dataType 设置返回的数据格式,常用的是'json'格式,也可以设置为'html'4、data 设置发送给服务器的数据5、success 设置请求成功后的回调函数6、error 设置请求失败后的回调函数7、async 设置是否异步,默认值是'true',表示异步
上海中信信息发展有限公司:
1.SSM之间有什么关系,各自的作用
1.持久层:DAO层(mapper层)(属于mybatis模块)
DAO层:主要负责与数据库进行交互设计,用来处理数据的持久化工作。
DAO层的设计首先是设计DAO的接口,也就是项目中你看到的Dao包。
然后在Spring的xml配置文件中定义此接口的实现类,就可在其他模块中调用此接口来进行数据业务的处理,而不用关心接口的具体实现类是哪个类,这里往往用到的就是反射机制,DAO层的jdbc.properties数据源配置,以及有 关数据库连接的参数都在Spring的配置文件中进行配置。
ps:(有的项目里面Dao层,写成mapper,当成一个意思理解。)2.业务层:Service层(属于spring模块)
Service层:主要负责业务模块的逻辑应用设计。也就是项目中你看到的Service包。
Service层的设计首先是设计接口,再设计其实现的类。也就是项目中你看到的service+impl包。
接着再在Spring的xml配置文件中配置其实现的关联。这样我们就可以在应用中调用Service接口来进行业务处理。
最后通过调用DAO层已定义的接口,去实现Service具体的实现类。
ps:(Service层的业务实现,具体要调用到已定义的DAO层的接口.)
3.控制层/表现层:Controller层(Handler层) (属于springMVC模块)
Controller层:主要负责具体的业务模块流程控制,也就是你看到的controller包。
Controller层通过要调用Service层的接口来控制业务流程,控制的配置也同样是在Spring的xml配置文件里面,针对具体的业务流程,会有不同的控制器。
4.View层 (属于springMVC模块)
负责前台jsp页面的展示,此层需要与Controller层结合起来开发。
Jsp发送请求,controller接收请求,处理,返回,jsp回显数据。
2.使用Oracle的驱动是什么
oracle数据库的驱动是固定的,连接时,直接写oracle.jdbc.driver.OracleDriver即可
3.MVC模式实现技术有哪些
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式:
Model(模型)表示应用程序核心(比如数据库记录列表)。
View(视图)显示数据(数据库记录)。
Controller(控制器)处理输入(写入数据库记录)。
MVC 模式同时提供了对 HTML、CSS 和 JavaScript 的完全控制。
Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。
通常模型对象负责在数据库中存取数据。
View(视图)是应用程序中处理数据显示的部分。
通常视图是依据模型数据创建的。
Controller(控制器)是应用程序中处理用户交互的部分。
通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
MVC 分层有助于管理复杂的应用程序,因为您可以在一个时间内专门关注一个方面。例如,您可以在不依赖业务逻辑的情况下专注于视图设计。同时也让应用程序的测试更加容易。
MVC 分层同时也简化了分组开发。不同的开发人员可同时开发视图、控制器逻辑和业务逻辑。
4.get和post的区别
5.redis缓存机制
数据库缓存:
sql语句时key值,查询结果resultSet是value,当同一个查询语句访问时(select * from t_product),只要曾经查询过,调用缓存直接返回resultSet,节省了数据库读取磁盘数据的时间。
持久层缓存:
减少了连接数据库的时间;减少了resultSet封装成对象的过程。
业务层和控制层的缓存:
减少调用层次。
描述缓存在业务层的逻辑:
查询商品信息
判断当前查询在缓存是否有数据
如果有数据,直接返回,当前请求结束;
如果没有数据,查询持久层数据库数据,获取数据存储再缓存一份,供后续访问使用;
缓存雪崩/缓存击穿
海量请求访问服务器,服务器的性能由缓存支撑,一旦一定范围的缓存数据未命中,请求的数据访问涌入数据库;承受不了压力造成宕机--重启--海量请求并未消失--宕机--重启,系统长时间不可用;这种情况就是缓存的雪崩。
1.==和equals()的区别
对于==,比较的是值是否相等如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;如果作用于引用类型的变量,则比较的是所指向的对象的地址
对于equals方法,注意:equals方法不能作用于基本数据类型的变量,
equals继承Object类,比较的是是否是同一个对象如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;
诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。
2.++i和i++的区别
i++是先赋值,然后再自增;++i是先自增,后赋值。
3.hibernate是通过什么对象访问数据库的
Hibernate,对于java来说很重要的一个东西,用于持久层。之前看了很多配置的,都不行,自己来写一个配置成功的。
环境:jdk1.8,eclipse-jee-oxygen,mysql-connector-java-5.1.46,hibernate 5.2.
首先要确保你的jdk,eclipse和mysql装好。然后下载jar包,mysql和hibernate的jar包。
然后安装JBoss插件,eclipse中Help-----eclipse market里面输入JBoss并搜索,找到JBoss之后,install,这里只需要对应hibernate的那些即可。
4.Oracle的存储方式
一、文件系统
(最简单的方式)
从本机存储划分出一部分空间给数据提供存储。
1.使用方式:
① 对这块区域做一个格式化(这是windows的称呼。在Linux和Unix系统中,叫创建文件系统。)
② 对磁盘进行挂载(windows中叫盘符) ## 对访问区域的一个接口
2.特点:
优点:数据文件容易访问,管理方便。
缺点:访问性能受到影响,中间多了一层文件系统
二、裸设备
该存储空间没有经过格式化,数据直接存放在该存储空间上。
普通用户无法访问,只有数据库软件才能够直接访问。
1.特点
优点:少了文件系统中间层,访问更加快捷,I/O性能比文件系统会提高20%
缺点:管理不方便,不能够cp,rf等操作。但是可以用Oracle工具处理(dd、RMAN)
三、ASM磁盘
ASM:Automatic Storage Management(Oracle 10g以后)
提供OMF管理方式:
手工建库时:db_create_file_dest=+DISK1
日志文件: db_create_logfile_dest=+DISK1
ASM磁盘,通过asm instance进行管理。数据库实例直接访问asm实例,这样访问方式更加紧密直接
Oracle 11g以后,允许操作系统和ASM之间进行交互
四、启动管理ASM的实例
1.编辑ASM初始化参数文件内容 $ORACLE_HOME/dbs/init+ASM.ora
*.background_dump_dest='/u01/app/oracle/admin/+ASM/bdump'
*.core_dump_dest='/u01/app/oracle/admin/+ASM/bdump'
*.instance_type='asm'
*.large_pool_size=12M
*.remote_login_passwordfile='SHARED'
*.user_dump_dest='/u01/app/oracle/admin/+ASM/bdump'
2.启用ASM实例
$ export ORACLE_SID=+ASM
$ sqlplus / as sysdba
SQL> startup nomount
ASM instance started
Total System Global Area 82736155 bytes
Fixed Size 6254372 byyes
Variable Size 73625362 bytes
ASM Cache 25173827 bytes
3.(第一次使用ASM)启动时会报错
ORA-29701 unable to connect to Cluster Manager
需要做如下处理:
$ cd $ORACLE_HOME/bin
$ su root(以root身份执行,但是不更改环境变量)
$ ./localconfig delete
$ ./localconfig add
5.裸设备绑定关系
/dev/raw/raw2: bound to major 58,minor 0
/dev/raw/raw3: bound to major 58,minor 1
/dev/raw/raw4: bound to major 58,minor 2
/dev/raw/raw5: bound to major 58,minor 3
6.创建磁盘组
create diskgroup disk1 normal redundancy
failgroup fg1 disk '/dev/raw/raw1' name d1
failgroup fg2 disk '/dev/raw/raw2' name d2
failgroup fg3 disk '/dev/raw/raw3' name d3
failgroup fg4 disk '/dev/raw/raw4' name d4
failgroup fg5 disk '/dev/raw/raw5' name d5
failgroup fg6 disk '/dev/raw/raw6' name d6;
注:external redundancy (主)表明冗余度仅要求一个故障组,假设这个磁盘对 于正在运行的数据库操作
normal redundancy 标准冗余度提供双向镜像,要求一个磁盘中要有两个故 障组
high redundancy 提供三向镜像,要求一个磁盘中要有三个磁盘组
create diskgroup disk1 external redundancy disk '/dev/raw/raw3';
在一个磁盘组中的各个磁盘中的文件,被粗糙的或精细的分割,粗糙分割为1M为单位分布于所有的磁盘中,适用于数据仓库,精细分割为128KB位单位分布文件,适用于OLTP。
7.查看新的可用磁盘组
SQL> select GROUP_NUMBER,name,type,total_mb,free_mb from v$asm_diskgroup;
SQL> select group_number,disk_number,name,failgroup,create_date,path from v$asm_disk;
8.删除磁盘组
drop diskgroup disk1
drop diskgroup disk1 including contents;(磁盘组中有数据库对象)
9.为磁盘组添加磁盘
alter diskgroup disk1 ass failgroup fg4 disk '/dev/raw/raw4' name d4;
10.从磁盘组中删除一个磁盘成员
alter diskgroup disk1 drop disk d4;
11.可以同时对磁盘组进行DROP和ADD操作,这样只发生一次平衡操作,减少CPU和I/O时间
aletr diskgroup disk1 add failgroup fg4 disk '/dev/raw/raw4' name d4 group disk d3;
1.hibernate如何处理sql语句
hibernate本来就支持 原sql 调用执行sql的方法就行了 调用方法的时候注意看所需的参数
@Autowiredprivate SessionFactory sessionFactory;
sessionFactory.getCurrentSession().createSQLQuery(sql);
2.JDBC是如何处理sql中传入的参数的
一、数组形式的参数
优点: 针对简单sql,可以节约部分内存空间。减少代码量、易读。
缺点:
1、相同的参数不可以复用,让array占用更多的空间。(当然,以现在的硬件来说,这点空间/内存、多余添加数组值花费的时间 完全微不足道)
2、如果是in的参数,要动态拼接(?)占位符。(个人认为最麻烦、繁琐的,扩展:oracle对in最大支持1000)
3、如果sql中参数过多,其实不好阅读修改。
个人习惯用List添加参数,然后再把List转换成Array。
好处是:如果用数组,当sql存在动态条件,那么无法确定数组长度。而用List就不需要自己去维护。
二、map形式的参数
优点:
1、解决了in参数的问题。
2、参数值可以复用。
三、javaBean形式的参数
如果参数是通过JavaBean传到dao层,那么不用把bean转换成map。相对的如果是通过map传到dao层的,也用map形式也无需转换成javaBean。
3.SVN中标记和标记合并的作用
SVN的“合并(Merge)”功能:这个功能是用来将分支(branch)与主干(Trunk)合并用的。通过自动合并,可以将大部分文件的变动合并到一起,形成一个集合全部改动的文件。
SVN是Subversion的简称,是一个开放源代码的版本控制系统,相较于RCS、CVS,它采用了分支管理系统,它的设计目标就是取代CVS。互联网上很多版本控制服务已从CVS迁移到Subversion。
4.使用vue.js开发
那么什么是Vue组件呢?它是vue.js最强大的功能之一,是可扩展的html元素,是封装可重用的代码,同时也是Vue实例,可以接受相同的选项对象(除了一些根级特有的选项) 并提供相同的生命周期钩子。
组件系统是Vue.js其中一个重要的概念,它提供了一种抽象,让我们可以使用独立可复用的小组件来构建大型应用,任意类型的应用界面都可以抽象为一个组件树:
那么什么是组件呢?
组件可以扩展HTML元素,封装可重用的HTML代码,我们可以将组件看作自定义的HTML元素。
使用组件的好处?
提高开发效率
方便重复使用
简化调试步骤
提升整个项目的可维护性
便于多人协同开发
1.Linux基本操作命令,如何查看文件内容
查看文件内容的命令:
cat 由第一行开始显示内容,并将所有内容输出
tac 从最后一行倒序显示内容,并将所有内容输出
more 根据窗口大小,一页一页的现实文件内容
less 和more类似,但其优点可以往前翻页,而且进行可以搜索字符
head 只显示头几行
tail 只显示最后几行
nl 类似于cat -n,显示时输出行号
tailf 类似于tail -f
1.cat 与 tac (不常用)
cat的功能是将文件从第一行开始连续的将内容输出在屏幕上。
cat语法:cat [-n] 文件名 (-n : 显示时,连行号一起输出)
tac的功能是将文件从最后一行开始倒过来将内容数据输出到屏幕上。
tac语法:tac 文件名。
2.more和less(常用)
more功能类似 cat ,cat命令是整个文件的内容从上到下显示在屏幕上。 more会以一页一页的显示方便使用者逐页阅读,而最基本的指令就是按空白键(space)就往下一页显示,按 b 键就会往回(back)一页显示,而且还有搜寻字串的功能 。more命令从前向后读取文件,因此在启动时就加载整个文件。
命令格式:
more [-dlfpcsu ] [-num ] [+/ pattern] [+ linenum] [file ... ]
命令功能:
more命令和cat的功能一样都是查看文件里的内容,但有所不同的是more可以按页来查看文件的内容,还支持直接跳转行等功能。
命令参数:+n 从笫n行开始显示-n 定义屏幕大小为n行+/pattern 在每个档案显示前搜寻该字串(pattern),然后从该字串前两行之后开始显示 -c 从顶部清屏,然后显示-d 提示“Press space to continue,’q’ to quit(按空格键继续,按q键退出)”,禁用响铃功能-l 忽略Ctrl+l(换页)字符-p 通过清除窗口而不是滚屏来对文件进行换页,与-c选项相似-s 把连续的多个空行显示为一行-u 把文件内容中的下画线去掉
常用操作命令:
Enter 向下n行,需要定义。默认为1行
Ctrl+F 向下滚动一屏
空格键 向下滚动一屏
Ctrl+B 返回上一屏= 输出当前行的行号
:f 输出文件名和当前行的行号
V 调用vi编辑器!命令 调用Shell,并执行命令
q 退出more
使用实例:
实例1:显示文件中从第3行起的内容
命令:
cat test.log #显示所有日志内容
more +3 test.log #从第三行开始显示日志内容
实例2.将日志内容设置为每屏显示4行
命令:
more -4 test.log
实例3.从文件中查找第一个出现"liu"字符串的行,并从该处前两行开始显示输出
命令:
more +/liu test.log
实例4.当一个目录下的文件内容太多,可以用more来分页显示。这得和管道 | 结合起来
命令:
cat test.log | more -5 #“|”表示管道,作用是可以将前面命令的输出当做后面命令的输入
less 工具也是对文件或其它输出进行分页显示的工具,应该说是linux正统查看文件内容的工具,功能极其强大。less 的用法比起 more 更加的有弹性。 在 more 的时候,我们并没有办法向前面翻, 只能往后面看,但若使用了 less 时,就可以使用 [pageup] [pagedown] 等按 键的功能来往前往后翻看文件,更容易用来查看一个文件的内容!除此之外,在 less 里头可以拥有更多的搜索功能,不止可以向下搜,也可以向上搜。
命令格式:
less [参数] 文件
命令功能:
less 与 more 类似,但使用 less 可以随意浏览文件,而 more 仅能向前移动,却不能向后移动,而且 less 在查看之前不会加载整个文件。
命令参数:-b <缓冲区大小> 设置缓冲区的大小-e 当文件显示结束后,自动离开-f 强迫打开特殊文件,例如外围设备代号、目录和二进制文件-g 只标志最后搜索的关键词-i 忽略搜索时的大小写-m 显示类似more命令的百分比-N 显示每行的行号-o <文件名> 将less 输出的内容在指定文件中保存起来-Q 不使用警告音-s 显示连续空行为一行-S 行过长时间将超出部分舍弃-x <数字> 将“tab”键显示为规定的数字空格/字符串:向下搜索“字符串”的功能?字符串:向上搜索“字符串”的功能
n:重复前一个搜索(与 / 或 ? 有关)
N:反向重复前一个搜索(与 / 或 ? 有关)
b 向后翻一页
d 向后翻半页
h 显示帮助界面
Q 退出less 命令
u 向前滚动半页
y 向前滚动一行
空格键 滚动一行
回车键 滚动一页
[pagedown]: 向下翻动一页
[pageup]: 向上翻动一页
使用实例:
实例1:ps查看进程信息并通过less分页显示同时显示行号
命令:
ps -ef|less -N
实例2.浏览多个文件
命令:
less test2.log test.log
说明:
输入 :n后,切换到 test.log
输入 :p 后,切换到test2.log
ps:当正在浏览一个文件时,也可以使用 :e命令 打开另一个文件。
命令:
less file1
:e file2
附加备注
(1)全屏导航
ctrl + F - 向前移动一屏
ctrl + B - 向后移动一屏
ctrl + D - 向前移动半屏
ctrl + U - 向后移动半屏
(2)单行导航
j - 向前移动一行
k - 向后移动一行
(3)其它导航
G - 移动到最后一行
g - 移动到第一行
q / ZZ - 退出 less 命令
(4)其它有用的命令
v - 使用配置的编辑器编辑当前文件
h - 显示 less 的帮助文档
&pattern - 仅显示匹配模式的行,而不是整个文件
(5)标记导航
当使用 less 查看大文件时,可以在任何一个位置作标记,可以通过命令导航到标有特定标记的文本位置:
ma - 使用 a 标记文本的当前位置
'a - 导航到标记 a 处
(6)查找
more, less 都具备查找功能,按/ 然后输入要找的字串,再按 Enter 即可,按 n(next) 会继续找,大写的 N 则是往回(上)找,按 q(quit)或者ZZ离开
3.head和tail(常用)
head和tail通常使用在只需要读取文件的前几行或者后几行的情况下使用。head的功能是显示文件的前几行内容
head的语法:head [n number] 文件名 (number 显示行数)
tail的功能恰好和head相反,只显示最后几行内容
tail的语法:tail [-n number] 文件名
查看日志文件的前1000行
1. head -n 1000 日志文件
查看日志文件最后1000行
tail -f -n 1000 日志文件 (实时打印最新的日志信息)
tail -1000n 日志文件(实时打印最新的日志信息)
tail -1000 日志文件(打印出日志文件的最后1000条信息)
tail -n +1000 日志文件(从第1000行开始显示日志信息)
查看日志文件的中间多少行
cat 日志文件 | head -n 3000 | tail -n + 10014.nl (不常用)
nl的功能和cat -n一样,同样是从第一行输出全部内容,并且把行号显示出来
nl的语法:nl 文件名
2.设计模式在编码过程中如何体现的
1、开闭原则:一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。a、抽象化的基本功能不变,不会被修改;b、把可变性的封装起来。2、里氏转换原则:父类可以被子类代替。里氏转换原则是对开闭原则的一个补充,违反里氏转换原则就是违反开闭原则,并且是代理模式的基础。
使用时需注意以下几点:
1、子类必须实现父类所有方法;
2、尽量把父类设计成抽象类或接口;
3、依赖倒转原则:抽象类不依赖于细节,细节应当依赖于抽象,高层模块不应当依赖于底层模块,都依赖与抽象。
4、接口隔离原则:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖于它不需要的那些接口;5、迪米特原则:一个对象应尽可能少的了解其他对象。各个对象建的耦合度比较低,都能够独立运行;6、聚合原则:用聚合类达到代码复用,而不是用继承关系来复用代码;7、单一原则:一个类只负责一个功能领域的相应职责。
南京微小宝:
1.jsp内置对象各个作用
1、request对象
request 对象是 javax.servlet.httpServletRequest类型的对象。 该对象代表了客户端的请求信息,主要用于接受通过HTTP协议传送到服务器的数据。(包括头信息、系统信息、请求方式以及请求参数等)。request对象的作用域为一次请求。
2、response对象
response 代表的是对客户端的响应,主要是将JSP容器处理过的对象传回到客户端。response对象也具有作用域,它只在JSP页面内有效。
3、session对象
session 对象是由服务器自动创建的与用户请求相关的对象。服务器为每个用户都生成一个session对象,用于保存该用户的信息,跟踪用户的操作状态。session对象内部使用Map类来保存数据,因此保存数据的格式为 “Key/value”。 session对象的value可以使复杂的对象类型,而不仅仅局限于字符串类型。
4、application对象
application 对象可将信息保存在服务器中,直到服务器关闭,否则application对象中保存的信息会在整个应用中都有效。与session对象相比,application对象生命周期更长,类似于系统的“全局变量”。
5、out 对象out 对象用于在Web浏览器内输出信息,并且管理应用服务器上的输出缓冲区。在使用 out 对象输出数据时,可以对数据缓冲区进行操作,及时清除缓冲区中的残余数据,为其他的输出让出缓冲空间。待数据输出完毕后,要及时关闭输出流。
6、pageContext 对象
pageContext 对象的作用是取得任何范围的参数,通过它可以获取 JSP页面的out、request、reponse、session、application 等对象。pageContext对象的创建和初始化都是由容器来完成的,在JSP页面中可以直接使用 pageContext对象。
7、config 对象
config 对象的主要作用是取得服务器的配置信息。通过 pageConext对象的 getServletConfig() 方法可以获取一个config对象。当一个Servlet 初始化时,容器把某些信息通过 config对象传递给这个 Servlet。 开发者可以在web.xml 文件中为应用程序环境中的Servlet程序和JSP页面提供初始化参数。
8、page 对象
page 对象代表JSP本身,只有在JSP页面内才是合法的。 page隐含对象本质上包含当前 Servlet接口引用的变量,类似于Java编程中的 this 指针。
9、exception 对象
exception 对象的作用是显示异常信息,只有在包含 isErrorPage="true" 的页面中才可以被使用,在一般的JSP页面中使用该对象将无法编译JSP文件。excepation对象和Java的所有对象一样,都具有系统提供的继承结构。exception 对象几乎定义了所有异常情况。在Java程序中,可以使用try/catch关键字来处理异常情况; 如果在JSP页面中出现没有捕获到的异常,就会生成 exception 对象,并把 exception 对象传送到在page指令中设定的错误页面中,然后在错误页面中处理相应的 exception 对象。
2.冒泡算法
1 public class MyBubbleSort { 2 3 public static void main(String[] args) { 4 int[] arr = {3, 2, 5, 1, 8, 1, 11, 8}; 5 int[] results = bubbleSort(arr); 6 for(int item : results){ 7 System.out.print(item + " "); 8 } 9 }10 11 /**12 * 冒泡排序,升序排列13 * 数组当中比较小的数值向下沉,数值比较大的向上浮!14 */15 public static int[] bubbleSort(int[] arr) {16 // 外层for循环控制循环次数17 for(int i=0;i<arr.length;i++){18 int tem = 0;19 // 内层for循环控制相邻的两个元素进行比较20 for(int j=i+1;j<arr.length;j++){21 if(arr[i]>arr[j]){22 tem = arr[j];23 arr[j]= arr[i];24 arr[i] = tem;25 }26 }27 }28 return arr;29 }30 }
南京实点实分网络科技:
1.JAVA序列化
----什么是序列化?--1--java序列化是指把java对象转换为字节序列的过程,而java反序列化是指把字节序列恢复为java对象的过程--2--序列化:对象序列化的最主要的用处就是在传递和保存对象的时候,保证对象的完整性和可传递性。序列化是把对象转换成有序字节流,以便在网络上传输或者保存在本地文件中。序列化后的字节流保存的java对象的状态以及相关的描述信息。序列化机制的核心作用就是对象状态的保存与重建。--3--反序列化:客户端从文件中或网络上获得序列化后的对象字节流后,根据字节流中所保存的对象状态及描述信息,通过反序列化重建对象。--4--序列化就是把实体对象状态按照一定的格式写入到有序字节流,反序列化就是从有序字节流重建对象,恢复对象状态
----为什么需要序列化与反序列化
当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本,图片,音频,视频等,而这些数据都会以二进制的形式在网络上传送。
当两个java进行进行通信时,要传送对象,怎么传对象,通过序列化与反序列化。
也就是说,发送方需要把对象转换为字节序列,然后在网络上传送,另一方面,接收方需要从字节序列中恢复出java对象
----序列化的好处--1--永久性保存对象,保存对象的字节序列到本地文件或者数据库中,实现了数据的持久化,通过序列化可以把数据永久的保存到硬盘上,--2--利用序列化实现远程通信,可以在网络上传送对象的字节序列。--3--在进程间传递对象
----序列化算法步骤--1--把对象实例相关的类元数据输出--2--递归输出类的超类描述直到不再有超类--3--类元数据完了以后,开始从最懂曾的超类开始输出对象实例的实际数据值。--4--从上至下递归输出实例的数据
----Java 如何实现序列化和反序列化--1-- JDK类库中序列化API
java.io.ObjectOutputStream: 表示输出对象流
它的writeObject(Object obj)方法可以对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中;--2--java.io.ObjectInputStream:表示对象输入流
它的readObject()方法源输入流中读取字节序列,再把它们反序列化成为一个对象,并将其返回
--2--实现序列化的要求
只有实现了Serializable或Externalizable接口的对象才能被序列化,否则抛出异常!
--3--实现java对象序列化与反序列化的方法
例 a 类,它的对象需要序列化,有3种方法
如果类a仅仅实现了Serializable接口,则
ObjectOutputStream采用默认的序列化方式,对a对象的非transient实例变量进行序列化
ObjectInputStream采用默认的反序列化方式,对a对象的非transient实例变量进行反序列化
如果类a仅仅实现了Serializable接口,并且还定义了a对象的writeObject(ObjectOutputStream out) 和readObject(ObjectInputStream in),则
ObjectOutputStream调用a对象的writeObject(ObjectOutputStream out)的方法进行序列化
ObjectInputStream调用a对象的readObject(ObjectInputStream in)的方法进行序列化
如果a类实现了ExternaInalizable接口,且User类必须实现readExternam(ObjectInput in)和wiriteExternal(ObjectOutput out)方法,则
ObjectOutputStream调用a对象的wiriteExternal(ObjectOutput out)的方法进行序列化
ObjectInputStream调用a对象的readExternam(ObjectInput in)的方法进行序列化‘’
----JDK类库中序列化的步骤--1--创建一个对象输出流,它可以包装一个奇特类型的目标输出流,如文件输出流:
objectOutputStream oos=new objectOutputStream(new FileOutStream(c:\\object.out));--2--通过对象输出流writeObject()方法写对象:
oos.writeObject(new a("xiaoxiao","145263","female"));
----JDK类库中反序列化的步骤--1--创建一个对象输入流,它可以包装一个其他类型输入流,如文件输入流:
objectInputStream ois=new ObjectInputStream(new FileInputStream("object.out"));--2--通过对象输出流的readObject()方法读取对象:
a aa=(a)ois.readObject();--3--为了正确读数据,完成反序列化,必须保证向对象输出流写对象的顺序与从对象输入流中读对象的顺序一致
2.如何以递归算法输出一个文件夹下的文件和文件目录
import java.io.File;
public class TestFile {
public static void main(String[] args) {
File f=new File("D:\\shixun");
printFile(f,0);
}
public static void printFile(File file,int level) {
//打印树状的层次关系
for(int i=0;i<level;i++) {
System.out.print("*");
}
//输出目录或者文件的名字
System.out.println(file.getName());
if(file.isDirectory()) {//判断file是否为目录
File [] listFiles=file.listFiles();
for(File temp:listFiles) {
//自己调用自己
printFile(temp,level+1);
}
}
}
}
3.Object的8种常用方法
getClass(), hashCode(), equals(), clone(), toString(), notify(), notifyAll(), wait(), finalize()
阿贝斯:
1.servlet的生命周期
Servlet 生命周期包括三部分:1.初始化:Web 容器加载 servlet,调用 init()方法2.处理请求:当请求到达时,运行其 service()方法。service()自动派遣运行与请求相对应的doXXX(doGet 或者 doPost)方法。3.销毁:服务结束,web 容器会调用 servlet 的 distroy()方法销毁 servlet。
2.map有哪些是线程安全的
JAVA中线程安全的map有:Hashtable、synchronizedMap、ConcurrentHashMap。
java中map中线程安全怎么实现:
同步的map就是Hashtable, concurrenthashmap。
你看到的Hashtable就是直接在hashmap上加了个锁,concurrenthashmap就是分成多个分段锁。
java代码中线程安全级别:
绝对线程安全。
在任何环境下,调用者都不需要考虑额外的同步措施,都能够保证程序的正确性。
这个定义要求很严格,java里面满足这个要求的类比较少,对于实现jsr133规范(java内存模型)的jdk(一般指jdk5.0之上),一般的不变类都是满足绝地线程安全的。比如 String,Integer类。一般情况下,定义了如果一个类里面所有字段都是final类型的,一般都认为这个类是不变的。不变类都是绝对线程安全的。
相对线程安全
在一般情况下,调用者都不需要考虑线程同步,大多数情况下,都能够正常运行。jdk里面大多数类都是相对安全的。最常见的例子是java里面Vector类。
3.上传文件使用的什么类来接收
尚哲智能:
1.什么是事务,作用是什么
一个事务是有下列属性的一个工作单元:
原子性(ATOMICITY):
一个事务要被完全的无二义性的做完或撤消。在任何操作出现一个错误的情况下,构成事务的所有操作的效果必须被撤消,数据应被回滚到以前的状态。
一致性(CONSISTENCY):
一个事务应该保护所有定义在数据上的不变的属性(例如完整性约束)。在完成了一个成功的事务时,数据应处于一致的状态。换句话说,一个事务应该把系统从一个一致-状态转换到另一个一致状态。举个例子,在关系数据库的情况下,
一个一致的事务将保护定义在数据上的所有完整性约束。
隔离性(ISOLATION):
在同一个环境中可能有多个事务并发执行,而每个事务都应表现为独立执行。串行的执行一系列事务的效果应该同于并发的执行它们。这要求两件事:
在一个事务执行过程中,数据的中间的(可能不一致)状态不应该被暴露给所有的其他事务。
两个并发的事务应该不能操作同一项数据。数据库管理系统通常使用锁来实现这个特征。
持久性(DURABILITY):
一个被完成的事务的效果应该是持久的。
2.SQL优化的方案
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:select id from t where num=0
3.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
4.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:select id from t where num=10 or num=20
可以这样查询:select id from t where num=10
union allselect id from t where num=20
5.in 和 not in 也要慎用,否则会导致全表扫描,如:select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:select id from t where num between 1 and 3
6.下面的查询也将导致全表扫描:select id from t where name like '%abc%'
7.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:select id from t where num/2=100
应改为:select id from t where num=100*2
8.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:select id from t where substring(name,1,3)='abc'--name以abc开头的id
应改为:select id from t where name like 'abc%'
9.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
10.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,
否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
11.不要写一些没有意义的查询,如需要生成一个空表结构:select col1,col2 into #t from t where 1=0
这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:
create table #t(...)
12.很多时候用 exists 代替 in 是一个好的选择:select num from a where num in(select num from b)
用下面的语句替换:select num from a where exists(select 1 from b where num=a.num)
13.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,
如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
14.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,
因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。
一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
15.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。
这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
16.尽可能的使用 varchar 代替 char ,因为首先变长字段存储空间小,可以节省存储空间,
其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
17.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
18.避免频繁创建和删除临时表,以减少系统表资源的消耗。
19.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。
20.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,
以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。
21.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。
22.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。
23.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
24.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。
在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。25.尽量避免大事务操作,提高系统并发能力。26.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
3.JAVA的内存模型
程序计数器:线程私有;记录指令执行的位置;
虚拟机栈:线程私有;生命周期和线程一致;存储局部变量表、操作数栈、动态链接、方法出口等信息。
本地方法栈:线程私有;为虚拟机使用到的 Native 方法服务。
堆:线程共享;JVM 所管理的内存中最大的一块区域,主要是存放对象实例和数组;
方法区:线程共享;存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
4.js中常用函数方法
1:常规写法//函数的写法
function run{
alert("常规写法") //这里是你函数的内容
}//调用
run()
2:匿名函数写法var run = function(){
alert("这是一种声明函数的写法,左边是一个变量,右边是一个函数的表达式,
意思就是把一个匿名函数的表达式赋值给了一个变量myrun,只是声明了一个变量指向了一个函数对象")//这里是你函数的内容
}
run()
3:将方法作为一个对象//作为对象方法,函数写法,这里创建了两个函数外面用{}包裹起来var Text = {
run1 : function(){
alert("这个必须放在一个对象内部,放在外边会出错")//这里是函数内容
},
run2 : function(){
alert("这个必须放在一个对象内部,放在外边会出错")//这里是函数内容
}
}
Text.run1()//调用第一个函数
Text.run2()//调用第二个函数
4.构造函数中给对象添加方法
javascript中的每个对象都有prototype属性,Javascript中对象的prototype属性的解释是:返回对象类型原型的引用。
// 给对象添加方法var funName = function(){};
funName.prototype.way = function(){
alert('这是在funName函数上的原始对象上加了一个way方法,构造函数中用到');
}
// 调用
var funname = new text();// 创建对象
funname.way();//调用对象属性
5.自执行函数
js自执行函数查到了几种不同写法,放上来给大家看看//方法1:最前最后加括号
(
function(){alert(1);}()
); /*这是jslint推荐的写法,好处是,能提醒阅读代码的人,这段代码是一个整体。
例如,在有语法高亮匹配功能的编辑器里,光标在第一个左括号后时,最后一个右括号也会高亮,看代码的人一眼就可以看到这个整体。 */
//方法2:function外面加括号
(function(){alert(1);})(); //这种做法比方法1少了一个代码整体性的好处。
//方法3:function前面加运算符,常见的是!与void 。
!function(){alert(1);}(); void function(){alert(2);}();
5.如何使用异常,异常有哪些
从根本上讲所有的异常都属于Throwable的子类,从大的方面讲分为Error(错误)和Exception(异常)。Eror是程序无法处理的异常,当发生Error时程序线程会终止运行。我们一般意义上讲的异常就是指的Exception,这也是面试官常问的问题。
下面就简单说一下关于Exception(以下都简称异常)的一点理解。
异常分为运行时异常(RuntimeException,又叫非检查时异常)和非运行时异常(又叫检查异常)。下面列举一下常见的运行时异常:
NullPointerException - 试图访问一空对象的变量、方法或空数组的元素
ArrayIndexOutOfBoundsException - 数组越界访问
NoClassDefFoundException - JAVA运行时系统找不到所引用的类
ArithmeticException - 算术运算中,被0除或模除
ArrayStoreException - 数据存储异常,写数组操作时,对象或数据类型不兼容
ClassCastException - 类型转换异常
IllegalArgumentException - 方法的参数无效
IllegalThreadStateException - 试图非法改变线程状态,比方说试图启动一已经运行的线程
NumberFormatException - 数据格式异常,试图把一字符串非法转换成数值(或相反)
SecurityException - 如果Applet试图执行一被WWW浏览器安全设置所禁止的操作
IncompatibleClassChangeException - 如改变了某一类定义,却没有重新编译其他引用了这个类的对象。如某一成员变量的声明被从静态改变为非静态,但其他引用了这个变量的类却没有重新编译,或者相反。如删除了类声明中的某一域或方法,但没有重新编译那些引用了这个域或方法的类
OutOfMemoryException - 内存不足,通常发生于创建对象之时
IncompatibleTypeException - 试图实例化一个接口,Java运行时系统将抛出这个异常
UnsatisfiedLinkException - 如果所需调用的方法是C函数,但Java运行时系统却无法连接这个函数
InternalException - 系统内部故障所导致的异常情况,可能是因为Java运行时系统本身的原因。如果发现一可重现的InternalException,可以直接给Sun公司发电邮。
6.spring中AOP和IOC
IOC:控制反转也叫依赖注入。利用了工厂模式
将对象交给容器管理,你只需要在spring配置文件总配置相应的bean,以及设置相关的属性,让spring容器来生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你需要调用这些bean的类(假设这个类名是A),分配的方法就是调用A的setter方法来注入,而不需要你在A里面new这些bean了。
注意:面试的时候,如果有条件,画图,这样更加显得你懂了.
AOP:面向切面编程。(Aspect-Oriented Programming)
AOP可以说是对OOP的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。
实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码.
7.JDK1.8有什么新特性
1、default关键字2、Lambda 表达式3、函数式接口4.方法与构造函数引用5、局部变量限制6、Date Api更新7、流
江苏金茂电子商务:
1.JAVA如何实现一个账号只能一个用户登录,其他人无法登录
1 .在用户登录时,把用户添加到一个ArrayList中2 .再次登录时查看ArrayList中有没有该用户,如果ArrayList中已经存在该用户,则阻止其登录3 .当用户退出时,需要从该ArrayList中删除该用户,这又分为三种情况
① 使用注销按钮正常退出
② 点击浏览器关闭按钮或者用Alt+F4退出,可以用JavaScript捕捉该页面关闭事件,
执行一段Java方法删除ArrayList中的用户
③ 非正常退出,比如客户端系统崩溃或突然死机,可以采用隔一段时间session没活动就删除该session所对应的用户来解决,这样用户需要等待一段时间之后就可以正常登录。
2.SQL查询一张表中重复数据的方法
1.查询出所有数据进行分组之后,和重复数据的重复次数的查询数据,先列下:select count(username) as '重复次数',username from xi group by username having count(*)>1 order by username desc2.查找表中多余的重复记录,重复记录是根据单个字段(peopleId)来判断select * from peoplewhere peopleId in (select peopleId from people group by peopleId having count(peopleId) > 1)3.查找表中多余的重复记录(多个字段)select * from people awhere (a.peopleId,a.seq) in (select peopleId,seq from people group by peopleId,seq having count(*) > 1)
3.如何实现批量更新
--创建表
create table test1
(
a varchar2(100),
b varchar2(100)
);
create table test2
(
a varchar2(100),
b varchar2(100)
);--插入语句
declare i number;
beginfor i in 1..10 loop
insert into test1(a,b)values(i,i);
end loop; for i in 1..5 loop
insert into test2(a,b)values(i,i||i);
end loop;
end; --查询一下:select t.*,t.rowid from test1 tselect t.*,t.rowid from test2 t --全都更新的方式:
update test1 t1set t1.b = (select t2.b from test2 t2 where t1.a = t2.a) --如果没有以下语句结果就不一样了,请注意where exists
(select '' from test2 t2 where t1.a = t2.a);--开始批量更新
declare
countpoint number := 0;
begin
for row_id in (select t1.rowid,t2.b from test1 t1,test2 t2 where t1.a = t2.a) loop
update test1 t1
set t1.b = row_id.b
where t1.rowid = row_id.rowid;
countpoint := countpoint + 1;
if mod(countpoint, 5) = 0 then
dbms_output.put_line('countpoint:' || countpoint);
commit;
end if;
end loop;
commit;
end; --删除数据
delete test1;
delete test2;--删除表
drop table test1;
drop table test2;
4.Switch函数中放什么类型的参数
jdk1.7以前的版本switch(expr1)中,expr1是一个整数表达式,整数表达式可以是int基本类型或Integer包装类型,由于,byte,short,char都可以隐含转换为int,
所以,这些类型以及这些类型的包装类型也是可以的。因此传递给 switch 和case 语句的参数应该是 int、 short、 char 或者 byte,还有enum。long,string 都不能作用于swtich。
在jdk 1.7中switch的参数类型可以是字符串类型。
南京数族公司:
1.java容器有哪些
数组,String,java.util下的集合容器
数组长度限制为 Integer.Integer.MAX_VALUE;
String的长度限制: 底层是char 数组 长度 Integer.MAX_VALUE 线程安全的
List:存放有序,列表存储,元素可重复
Set:无序,元素不可重复
Map:无序,元素可重复
2.第三方支付接口用过没讲一讲
3.hashtable和hashmap的区别
1、继承的父类不同
Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但二者都实现了Map接口。
2、线程安全性不同
javadoc中关于hashmap的一段描述如下:此实现不是同步的。如果多个线程同时访问一个哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。
Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省情况下是非Synchronize的。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步,
但使用HashMap时就必须要自己增加同步处理。(结构上的修改是指添加或删除一个或多个映射关系的任何操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)
这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。最好在创建时完成这一操作,
以防止对映射进行意外的非同步访问。
Map m = Collections.synchronizedMap(new HashMap(...));
Hashtable 线程安全很好理解,因为它每个方法中都加入了Synchronize。这里我们分析一下HashMap为什么是线程不安全的:
HashMap底层是一个Entry数组,当发生hash冲突的时候,hashmap是采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入。
4.red3is持久化
RDB 持久化:该机制可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)。
AOF 持久化:记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。AOF
文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。 Redis 还可以在后台对 AOF 文件
进行重写(rewrite),使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小
无持久化:让数据只在服务器运行时存在。
同时应用 AOF 和 RDB:当 Redis 重启时, 它会优先使用 AOF 文件来还原数据集, 因为 AOF 文件保存的
数据集通常比 RDB 文件所保存的数据集更完整
5.hashmap线程不安全,如何使他安全
6.token存redis时,redis崩溃怎么办
7.mycat分库分表
1、分区
对业务透明,分区只不过把存放数据的文件分成了许多小块,例如mysql中的一张表对应三个文件.MYD,MYI,frm。
根据一定的规则把数据文件(MYD)和索引文件(MYI)进行了分割,分区后的表呢,还是一张表。分区可以把表分到不同的硬盘上,但不能分配到不同服务器上。
优点:数据不存在多个副本,不必进行数据复制,性能更高。
缺点:分区策略必须经过充分考虑,避免多个分区之间的数据存在关联关系,每个分区都是单点,如果某个分区宕机,就会影响到系统的使用。2、分片
对业务透明,在物理实现上分成多个服务器,不同的分片在不同服务器上。如HDFS。3、分表
同库分表:所有的分表都在一个数据库中,由于数据库中表名不能重复,因此需要把数据表名起成不同的名字。
优点:由于都在一个数据库中,公共表,不必进行复制,处理更简单。
缺点:由于还在一个数据库中,CPU、内存、文件IO、网络IO等瓶颈还是无法解决,只能降低单表中的数据记录数。表名不一致,会导后续的处理复杂(参照mysql meage存储引擎来处理)
不同库分表:由于分表在不同的数据库中,这个时候就可以使用同样的表名。
优点:CPU、内存、文件IO、网络IO等瓶颈可以得到有效解决,表名相同,处理起来相对简单。
缺点:公共表由于在所有的分表都要使用,因此要进行复制、同步。一些聚合的操作,join,group by,order等难以顺利进行。4、分库
分表和分区都是基于同一个数据库里的数据分离技巧,对数据库性能有一定提升,但是随着业务数据量的增加,原来所有的数据都是在一个数据库上的,
网络IO及文件IO都集中在一个数据库上的,因此CPU、内存、文件IO、网络IO都可能会成为系统瓶颈。
当业务系统的数据容量接近或超过单台服务器的容量、QPS/TPS接近或超过单个数据库实例的处理极限等。此时,往往是采用垂直和水平结合的数据拆分方法,把数据服务和数据存储分布到多台数据库服务器上。
分库只是一个通俗说法,更标准名称是数据分片,采用类似分布式数据库理论指导的方法实现,对应用程序达到数据服务的全透明和数据存储的全透明
8.linux常用命令
https://blog.csdn.net/qq_23329167/article/details/83856430
1
、 用户发送请求至前端控制器DispatcherServlet。
2
、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3
、 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4
、 DispatcherServlet调用HandlerAdapter处理器适配器。
5
、 HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
6
、 Controller执行完成返回ModelAndView。
7
、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
8
、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
9
、 ViewReslover解析后返回具体View。
10
、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11
、 DispatcherServlet响应用户。
10.springboot常用注解
1、@Component:放在类上,把普通类实例化到spring容器中。可以说很多注解都是基于这个注解的。
2、@Bean: 放在方法上,用@Bean标注方法等价于XML中配置bean,这个方法一般返回一个实体对象,告诉spring这里产生一个对象,然后这个对象会交给Spring管理。产生这个对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的容器中。
3、@Configuration:标注当前类是配置类,并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到srping容器中,并且实例名就是方法名。(其实就是靠@Component注解)
4、@ConfigurationProperties:将配置文件中的参数映射成一个对象,通过prefix来设定前缀,然后将后面的和对象的属性名一致就能实现注入(当然这个对象需要注入的属性需要提供get和set方法 - - - 因为spring底层其实就是通过反射调用该对象的set方法)
11.zookeeper
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。
它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
ZooKeeper包含一个简单的原语集, [1] 提供Java和C的接口。
ZooKeeper代码版本中,提供了分布式独享锁、选举、队列的接口,代码在$zookeeper_home\src\recipes。其中分布锁和队列有Java和C两个版本,选举只有Java版本。
家哇网络:
1.ArrayList和LinkedList的区别
ArrayList 是动态数组结构,有索引,查询快(时间复杂度O(1)),增删慢(因为要移动索引)
LinkedList是链表结构,无索引,有指向前后的指针,查询需要从头开始向下寻找(时间复杂度O(n)),增删快(只需要修改链表中指针的指向,不需要移动其他)
2.requestBody和resourceBody的作用
@RequestBody:
作用:
主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的);
3.如何实现上传文件
4.JAVA如何实现定时任务
普通thread
这是最常见的,创建一个thread,然后让它在while循环里一直运行着,通过sleep方法来达到定时任务的效果。这样可以快速简单的实现。
用Timer和TimerTask
上面的实现是非常快速简便的,但它也缺少一些功能。
用Timer和TimerTask的话与上述方法相比有如下好处:
当启动和去取消任务时可以控制
第一次执行任务时可以指定你想要的delay时间
在实现时,Timer类可以调度任务,TimerTask则是通过在run()方法里实现具体任务。
Timer实例可以调度多任务,它是线程安全的。
当Timer的构造器被调用时,它创建了一个线程,这个线程可以用来调度任务:
ScheduledExecutorService
ScheduledExecutorService是从Java SE 5的java.util.concurrent里,做为并发工具类被引进的,这是最理想的定时任务实现方式。
相比于上两个方法,它有以下好处:
相比于Timer的单线程,它是通过线程池的方式来执行任务的
可以很灵活的去设定第一次执行任务delay时间
提供了良好的约定,以便设定执行的时间间
5.文件流有哪些
字节流和字符流
1.String是最基本的数据类型吗?String和StringBuffer的区别?
不是。Java中的基本数据类型只有8个:byte,short,int,long,float,double,char,boolean,除了基本类型(primitive type),剩下的都是引用类型(reference type),枚举类型(enumeration type)也是引用类型。其中 byte 一个字节,short两个字节,int四个字节,long八个字节,char两个字节。
区别:
(1)String:是对象不是原始类型.为不可变对象,一旦被创建,就不能修改它的值.对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去.String 是final类,即不能被继承。
(2)StringBuffer:是一个可变对象,当对他进行修改的时候不会像String那样重新建立对象。它只能通过构造函数来建立对象被建立以后,在内存中就会分配内存空间,并初始保存一个null.向StringBuffer中付值的时候可以通过它的append方法.
2.int和Integer有什么区别?
int是基本数据类型,Integer是引用数据类型;
int默认值是0,Integer默认值是null;
int类型直接存储数值,Integer需要实例化对象,指向对象的地址。
3.运行时异常与一般异常有何异同?
1.定义不同,运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等。一般异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。2.处理方法不同,运行时异常是不检查异常,程序中可以选择捕获处理,也可以不处理。对于一般异常,JAVA编译器强制要求用户必需对出现的这些异常进行catch并处理,否则程序就不能编译通过。3.发生原因不同,运行时异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。面对这种异常不管我们是否愿意,只能自己去写一大堆catch块去处理可能的异常。
4.说出Servlet的生命周期,并说出Servlet和CGI的区别?
服务器启动时(web.xml中配置load-on-startup=1,默认为0)或者第一次请求该servlet时,就会初始化一个Servlet对象,也就是会执行初始化方法init(ServletConfig conf),该servlet对象去处理所有客户端请求,service(ServletRequest req,ServletResponse res)方法中执行,最后服务器关闭时,才会销毁这个servlet对象,执行destroy()方法。
区别:1)较好的可移植性。由于Java语言具有跨平台和可移植性强的特点,使得Sevlet也有较好的可移植性, 即无须修改代码就可以部署到多种不同类型的Web服务器上。2)执行效率高。由于CGI针对每个请求都会创建一一个进程来处理, 而Servlet针对每个请求创建一个线程来执行,而创建线程比创建进程的开销要小,所以,与CGI相比,Servlet在交互过程中有更短的响应时间, 响应效率更高。3)功能强大。Servlet可以与Web服务器进行交互,而CGI却无法与Web服务器直接交互。4)使用方便。Servlet 提供了许多非常有用的接口用来读取或设置HTTP头消息,处理Cookie和跟踪会话状态等。5)可扩展性强。由于Servlet是由Java语言编写的,所以,它具备了Java 语言的所有优势
5.说出ArrayList,Vector,LinkedList的存储性能和特性。
ArrayList 和 Vector 都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector 由于使用了 synchronized 方法(线程安全),
通常性能上较 ArrayList 差,而LinkedList 使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。
中科软:
1.struts 核心控制器
Struts1:
ServletAction继承于HttpServlet,是struts1.x中和核心控制器。
Struts 2:
FilterDispatcher是Struts 2框架的核心控制器,该控制器作为一个Filter运行在Web应用中,它负责拦截所有的用户请求,当用户请求到达时,该Filter会过滤用户请求。如果用户请求以action结尾,该请求将被转入Struts 2框架处理。
2.hibernate和mybatis哪个好用为什么 线程实现方式
3.jstl
JSTL(Java server pages standarded tag library,即JSP标准标签库)是由JCP所制定的标准规范,它主要提供给Java Web开发人员一个标准通用的标签库,开发人员可以利用这些标签取代JSP页面上的Java代码,从而提高程序的可读性,降低程序的维护难度。
4.集合
集合有两个父接口,一个collection,一个Map;
而collection有两个子接口,一个List,一个Set;
List有两个常见的实现类 ArrayList,LinkedList;
Set有两个常见的实现类 HashSet,TreeSet;
Map有两个常见的实现类 HashMap,HashTable。
5.过滤器 拦截器
1.过滤器(Filter):过滤器就是过滤的作用,在web开发中过滤一些我们指定的url,比如过拦截掉我们不需要的接口请求,修改请求(request)和响应(response)内容,完成CORS跨域请求等等2.拦截器(Interceptor):是面向切面编程(AOP,Aspect Oriented Program)的。就是在Service或者一个方法前调用一个方法,或者在方法后调用一个方法。比如动态代理就是拦截器的简单实现,在调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在调用方法后打印出字符串,甚至在抛出异常的时候做业务逻辑的操作。
6.jsp内置对象
1、request对象
request 该对象代表了客户端的请求信息2、response对象
response 代表的是对客户端的响应,3、session对象
session 对象是由服务器自动创建的与用户请求相关的对象。4、application对象
application 对象可将信息保存在服务器中,直到服务器关闭,5、out 对象
out 对象用于在Web浏览器内输出信息,并且管理应用服务器上的输出缓冲区。6、pageContext 对象
pageContext 对象的作用是取得任何范围的参数7、config 对象
config 对象的主要作用是取得服务器的配置信息。8、page 对象
page 对象代表JSP本身,只有在JSP页面内才是合法的。 9、exception 对象
exception 对象的作用是显示异常信息
7.四大作用域
1. page域: 只能在当前jsp页面使用 (当前页面) 2. request域: 只能在同一个请求中使用 (转发) 3. session域: 只能在同一个会话(session对象)中使用 (私有的) 4. context域: 只能在同一个web应用中使用 (全局的)
8.Ajax
AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
中科软
1.一个单例
单例模式
public class Singleton {
private Singleton(){
}
private static volatile Singleton instance = null;
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
2.介绍线程死锁
线程死锁,当线程为了安全及同步时,用全局信号 量进行相互等待或是相互制约时程序 进入 了一个死锁状态 时(两个线程都停掉了)叫线程死锁。
3.AJAX怎么写有什么用
$.ajax({
type: "POST",
url: "ShowProduct.aspx/GetDHList",
data: '{CategoryId:"' + CategoryId + '"}',
contentType: "application/json;charset=utf-8",
dataType: "json",
success: function (data) {
var item = eval(data.d);
var StrHtml = "";for (var i in item) {}
}, async: false
});
第二种:
$(document).ready(function(){
$("#b01").click(function(){
htmlobj=$.ajax({url:"/jquery/test1.txt",async:false});
$("#myDiv").html(htmlobj.responseText);
});
});
第三种:
$.ajax({ url: "test.html", context: document.body, success: function(){
$(this).addClass("done");
}});
4.XML解析方式
DOM(Document Object Model) 生成和解析XML文档。由W3C提供的接口,它将整个XML文档读入内存,构建一个DOM树来对各个节点(Node)进行操作。
SAX(Simple API for XML) 生成和解析XML文档。SAX不用将整个文档加载到内存,基于事件驱动的API(Observer模式),用户只需要注册自己感兴趣的事件即可。
DOM4J生成和解析XML文档。dom4j是目前在xml解析方面是最优秀的(Hibernate、Sun的JAXM也都使用dom4j来解析XML),它合并了许多超出基本 XML 文档表示的功能,包括集成的 XPath 支持、XML Schema 支持以及用于大文档或流化文档的基于事件的处理
JDOM生成和解析XML 。 JDOM与DOM非常类似,它是处理XML的纯JAVA API,API大量使用了Collections类,且JDOM仅使用具体类而不使用接口。
5.JDK与JRE
JDK与JRE的区别
JRE: Java Runtime Environment
JDK:Java Development Kit
JRE顾名思义是java运行时环境,包含了java虚拟机,java基础类库。是使用java语言编写的程序运行所需要的软件环境,是提供给想运行java程序的用户使用的。
JDK顾名思义是java开发工具包,是程序员使用java语言编写java程序所需的开发工具包,是提供给程序员使用的。JDK包含了JRE,同时还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具:jconsole,jvisualvm等工具软件,还包含了java程序编写所需的文档和demo例子程序。
6.内存回收机制
分配内存与回收内存的标准是八个字:分代分配,分代回收。
由于字符串、对象和数组没有固定大小,所有当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用。
7.断点快捷键
快捷键 功能描述
F8 单步调试,不进入函数内部
F7 单步调试,进入函数内部
Shift+F7 选择要进入的函数
Shift+F8 跳出函数
Alt+F9 运行到断点
Alt+F8 执行表达式查看结果
F9 继续执行,进入下一个断点或执行完程序
Ctrl+F8 设置/取消当前行断点
Ctrl+Shift+F8 查看断点
8.自动装箱拆箱
自动装箱,就是自动将基本数据类型转换为包装器类型;自动拆箱,就是自动将包装器类型转换为基本数据类型
9.jquery
jQuery是一个快速、简洁的JavaScript框架,它封装JavaScript常用的功能代码,提供一种简便的JavaScript设计模式,优化HTML文档操作、事件处理、动画设计和Ajax交互。
10.jsp指令和动作各自原理
三个指令:
page
include
taglib 标签指令
六个动作:< jsp:include > 动态包含< jsp:forward> 请求转发 < jsp:param> 设置请求参数< jsp : useBean>
< jsp : setProperty> 给指定的对象属性赋值 < jsp : getProperty >取出指定对象的属性值
中科软
1.css样式怎么定义
1.新建一个HTML页面,命名为test.html,用于对CSS样式定义的说明。2.在test.html页面定义一个div元素,并且通过<style type="text/...
3.给div定义CSS样式,可以直接使用元素div作为选择器来实现样式定义。
2.oracle分页
String sql =
"select * from " +
(select *,rownum rid from (select * from students order by postime desc) where rid<=" + pagesize*pagenumber + ") as t" +
"where t>" + pageSize*(pageNumber-1);
3.8种基本类型
4.final,finally,finalize的区别
一、final :1、修饰符(关键字) 如果一个类被声明为final,意味着它不能再派生新的子类,不能作为父类被继承。2、将变量或方法声明为final,可以保证他们使用中不被改变。被声明为final的变量必须在声明时给定初值,而以后的引用中只能读取,不可修改,被声明为final的方法也同样只能使用,不能重载。
二、finally:
在异常处理时提供finally块来执行清楚操作。如果抛出一个异常,那么相匹配的catch语句就会执行,然后控制就会进入finally块,如果有的话。
三、finalize:
是方法名。java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除之前做必要的清理工作。这个方法是在垃圾收集器在确定了,被清理对象没有被引用的情况下调用的。
finalize是在Object类中定义的,因此,所有的类都继承了它。子类可以覆盖finalize()方法,来整理系统资源或者执行其他清理工作。
5.怎么启动线程
继承Thread类1.自定义类MyThread继承Thread类2.MyThread类重写run方法。
实现Runnable接口1.自定义类MyRunnable实现Runnable接口2.重写run()方法3.创建MyRunnable类的对象4.创建Thread类的对象,并把步骤3创建的对象作为构造参数传递5.启动线程
6.java为什么是跨平台的
因为Java程序编译之后的代码不是能被硬件系统直接运行的代码,而是一种“中间码”——字节码。然后不同的硬件平台上安装有不同的Java虚拟机(JVM),由JVM来把字节码再“翻译”成所对应的硬件平台能够执行的代码。因此对于Java编程者来说,不需要考虑硬件平台是什么。所以Java可以跨平台。
7.get,post请求的区别
1、 Get请求提交的数据会在地址栏显示出来,而post请求不会再地址栏显示出来2、 传输数据的大小
http Get请求由于浏览器对地址长度的限制而导致传输的数据有限制。而POST请求不会因为地址长度限制而导致传输数据限制。3、 安全性,POST的安全性要比GET的安全性高。由于数据是会在地址中呈现,所以可以通过历史记录找到密码等关键信息。
8.list和set的区别
List是有序的Collection。Java List一共三个实现类: 分别是ArrayList、Vector和LinkedList。
Set是唯一的,不可重复的,常用的实现类有HashSet、TreeSet、LinkedHashSet
9.hashmap怎么用
HashMap根据键的hashCode值存储数据,大多数情况下可以直接定位到它的值,因而具有很快 的访问速度,但遍历顺序却是不确定的。 HashMap最多只允许一条记录的键为null,允许多条记 录的值为 null。HashMap 非线程安全。
10.还有ajax怎么实现同步和异步
AJAX中根据async的值不同分为同步(async = false)和异步(async = true)
11.还有文件传输怎么传
第一种:以字符串传参的方式发送到指定的接口取出其值(多个就采用数组)
第二种:采用流传输的方式传接口,这种可以批量传输且,大小无限制,支持多种类型文件上传
12.sevrlet 生命周期
①服务器加载Sevrlet。
②创建Sevrlet,只有在请求Sevrlet时,才会调用构造方法,创建实例。
③init()初始化,创建Sevrlet实例后立即被调用,且只执行一次。
④service()处理用户请求,可以被多次调用,每次请求都会调用service方法,实际用于响应请求的。
⑤destory()销毁,只被调用一次,在当前Servlet所在的WEB应用被卸载前调用,用于释放当前Servlet所占用的资源
1.XML映射中,如何使用LIKE进行模糊查询
1.直接传参法 直接传参法,就是将要查询的关键字keyword,在代码中拼接好要查询的格式,如%keyword%,然后直接作为参数传入mapper.xml的映射文件中。
2.CONCAT()函数 MySQL的 CONCAT()函数用于将多个字符串连接成一个字符串,是最重要的mysql函数之一。
3.使用mybatis标签<bind value='' name=''/>
4.在mybatis中xml文件中配置 name LIKE '%${name}%' 或者是 address LIKE "%"#{address}"%"
2.XML映射中,如何批量插入传入的集合(Collection<E>)?,即如何根据集合生成如下SQL?
INSERT INTO TABLE_NAME(a,b,c) VALUES (a1,b1,c1),(a2,b2,c2)
3.XML映射中,如何在插入的同时获取MYSQL自增字段(AUTO_INCREMENT)生成的值?
使用select last_insert_id()
@Insert("insert into table(content,questionid,active,submit,createtime) values(#{content},#{questionid},1,#{submit},#{createtime})")
@SelectKey(statement="select last_insert_id()",before=false,keyProperty="_id",resultType=Integer.class,keyColumn="_id")
int insertQuestionItem(QuestionItemInfo questionItemInfo);
4.BlockingQueue相比普通的Queue最大的区别是什么?
阻塞队列是与普通队列的区别有两点
1.阻塞队列获取元素时,如果队列为空,则会等待队列有元素,否则就阻塞队列(普通队列返回结果,无元素)
2.阻塞队列放入元素时,如果队列满,则等待队列,直到有空位置,然后插入。(普通队列,要么直接扩容,要么直接无法插入,不阻塞)
阻塞队列的最佳场景就是生产者和消费者,使用代码时无需判断直接获取并处理(普通队列得判断有没有元素,阻塞队列不用判断,无元素自身就会阻塞,直到有东西)
5.Thread.sleep()可能抛出的InterruptedException代表什么?如何处理?
当一个方法后面声明可能会抛出InterruptedException异常时,说明该方法是可能会花一点时间,但是可以取消的方法。
抛InterruptedException的代表方法有:
1.Java.lang.Object类的wait方法
2.Java.lang.Thread类的sleep方法。
3.Java.lang.Thread类的join方法。
这里我们只看sleep方法。
interrupt方法是Thread类的实例方法,在执行的时候并不需要获取Thread实例的锁定,任何线程在任何时刻,都可以通过线程实例来调用其他线程的interrupt方法。当在sleep中的线程被调用interrupt方法时,就会放弃暂停的状态,并抛出InterruptException异常,这样一来,线程的控制权就交给了捕获这个异常的catch块了。
Interrupt方法的作用-------
interrupt方法只是改变了中断状态
sleep方法内部会不断的检查中断状态的值,从而自己抛出InterruptedException.
由 Thread 提供并受 Thread.sleep() 和 Object.wait() 支持的中断机制就是一种取消机制;它允许一个线程请求另一个线程停止它正在做的事情。当一个方法抛出 InterruptedException 时,它是在告诉您,如果执行该方法的线程被中断,它将尝试停止它正在做的事情而提前返回,并通过抛出 InterruptedException 表明它提前返回。 行为良好的阻塞库方法应该能对中断作出响应并抛出 InterruptedException,以便能够用于可取消活动中,而不至于影响响应。
————————————————
版权声明:本文为CSDN博主「Silvia_flora」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lilylove1994/article/details/78964330
6.如何实现一个方法,该方法执行某项任务并设定某个时间限制,如果该任务能在限制时间内完成则返回任务执行结果,否则返回null?写出核心代码
ScheduledExecuterService service = Executors.newSinglThreadSchduledExecetor();
ScheduledFuture f = service.scheduleAtFixedDelay(task, initialDelay, period, time unit);
if currentDate = 00:00
f.cancel()
service.shutdown()
7.
a.下述代码在多线程环境中是否存在问题?若有,如何修正?
b.下述代码中的volatile关键字是什么意思?如果删除对该段代码有何影响?
class Counter{
private volatile int count = 0;
public int getNext(){
return ++count;
}
}
8.@Controller中,跳转("redirect:url")和转发("forward:url")有什么区别?
1.从地址栏显示来说
forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址.
redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.2.从数据共享来说
forward:转发页面和转发到的页面可以共享request里面的数据.
redirect:不能共享数据.3.从运用地方来说
forward:一般用于用户登陆的时候,根据角色转发到相应的模块.
redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等.4.从效率来说
forward:高.
redirect:低.
9.
10.使用注解(如@Component)声明Bean,如何指定Bean加载顺序?
@Primary 注解
当系统中需要配置多个具有相同类型的bean时,@Primary可以定义这些Bean的优先级
11.多个同类型的Bean,使用注解注入时如何指定?
@Qualifier 注解
当系统中存在同一类型的多个Bean时,@Autowired在进行依赖注入的时候就不知道该选择哪一个实现类进行注入。此时,我们可以使用@Qualifier注解来微调,帮助@Autowired选择正确的依赖项。下面是一个关于此注解的代码示例:
。下面将给出一个实例代码来说明这一特性:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。