模拟面试面试题汇总

模拟面试面试题汇总 

第一轮面试题

目录

模拟面试面试题汇总 

第一轮面试题

一、描述下数据库中的事务--ACID各个的特点。

二、什么是springboot?你们公司是用的哪个版本?

第二轮面试题

第三轮面试题

第四轮面试题

第五轮面试

1.死信、延迟、重试队列

8.微服务

9.java锁机制的问题

英斯特笔试:

软通动力电话面试:

多飞网络科技:

北京爱朗格瑞:

掌缘信息科技:

中科软 

端木公司


 

一、描述下数据库中的事务--ACID各个的特点。

原子性:事务中的操作要么全部成功要么全部失败。
一致性:事务前后数据的完整性必须保持一致。
隔离性:多个并发的事务之间是相互隔离的,互不干扰的。
持久性:事务提交后,数据是永久改变的。

 

二、什么是springboot?你们公司是用的哪个版本?

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?

DubboSpringcloud都是分布式服务中常用的框架。dubbocloud不同,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.扫描包

2.mvc驱动

3.事务驱动

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?

Portainer是Docker的图形化管理工具

 

七、在公司里使用的docker的版本是什么?

18.09.7

 

八、镜像和容器的关系?

容器是可写的,镜像是可读的。容器是镜像的实例,镜像是容器的静态备份。

 

九、分布式锁中的基于redissetnx的原理以及setsetnx的区别是什么?

分布式锁基于setnx原理,如果 SETNX 返回1,说明该进程获得锁。
如果 SETNX 返回0,说明其他进程已经获得了锁,进程不能进入临界区。进程可以在一个循环中不断地尝试 SETNX 操作,以获得锁。

redisset是将字符串值 value 关联到 key 。如果 key 已经持有其他值, SET 就覆写旧值,无视类型。

setnx是当且仅当 key 不存在时,可以设置 value 。若给定的 key 已经存在,则 SETNX 不做任何动作。

 

十、什么是springcloudspringcloud中的组件有哪些?分别描述下它的原理?

SpringCloud架构中最核心的是Eureka注册中心,它是一个通讯录。所有p端和c端都会注册在上面,cloudc端调用p端应用feign组件进行调用。应用程序导致网络请求出现问题时,hystrix组件实现容错机制。用户请求量变大是,用ribbon组件实现客户端的负载均衡。而服务器端的负载均衡通常使用nginx反向代理。SpringCloud中有类似于servlet过滤器的组件,zuul网关组件对整个服务架构进行安全过滤。当使用一个服务调用多个feign组件的时候,sleuth+zipkin组件用来服务追踪,可以用来调试接口(追踪问题)。config组件进行统一配置。

dubbo基于RPCSpringCloud基于RESTful

组件ribbonfeignhstrix eurekazuul

 

 

十一、什么是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

SSM搭建的版本有很多,例如有一个版本可以这么搭建,两个核心配置文件web.xml,applicationContext.xml。

1.前端控制器DispatcherServlet

2.过滤器CharacterEncodingFilter

applicationContext.xml

1.扫描包

2.mvc驱动

3.事务驱动

4.配置数据源

5.SqlSessionFactoryBean    

6.配置事务

7.数据映射器

8.视图解析器  

五、什么是集合?

集合有两个父接口,一个Collection,一个Map,Collection有两个子接口,一个List,一个SetList有两个常见的实现类ArrayList,LinkedList,
Set有两个常见的实现类HashSetTreeSetMap有两个常见的实现类HashMapHashTable

 

六、什么是SpringCloud?

SpringCloud架构中最核心的是Eureka注册中心,它是一个通讯录。所有p端和c端都会注册在上面,cloudc端调用p端应用feign组件进行调用。应用程序导致网络请求出现问题时,hystrix组件实现容错机制。用户请求量变大时,用ribbon组件实现客户端的负载均衡。而服务器端的负载均衡通常使用nginx反向代理。SpringCloud中有类似于servlet过滤器的组件,zuul网关组件对整个服务架构进行安全过滤。

dubbo基于RPCSpringCloud基于RESTful

 

七、什么是Dubbo?

DubboSpringcloud都是分布式服务中常用的框架。dubbocloud不同,dubbo基于RPC协议。它提供了三大核心功能: 面向接口的远程方法调用,

智能容错和负载均衡, 以及服务自动注册和发现。

Dubbo按照分层的方式来架构,可以最大限度地松耦合。Dubbo采用一种非常简单的模型, 要么提供方提供服务, 要么是消费方消费服务, 所以基于这一点可以抽象出服务提供方和服务消费方两个角色。

 

八、什么是消息中间件?

消息中间件利用高效可靠的消息传递机制进行异步的数据传输,并基于数据通信进行分布式系统的集成。通过提供消息队列模型和消息传递机制,可以在分布式环境下扩展进程间的通信。

 

九、eurekazookeeper的区别?

首先他们都是服务注册和发现的功能,cloud一般选择eureka作为注册中心,dubbo一般选择zookeeper作为注册中心。基于CAP原则,eurekAP原则,即可用性和分区容错性,zookeeperCP原则,即强一致性和分区容错性。

 

十、分布式锁的概念?

在分布式系统中,由于分布式系统的分布性,即多线程和多进程并且分布在不同机器中,这两种锁将失去原有锁的效果,需要我们自己实现分布式锁——分布式锁。
在多线程的环境下,多个线程同时访问同一个资源,导致线程不安全,使用分布式锁来解决这个线程安全问题。

分布式抢购用到分布式锁,上锁;第二个用户要等待第一个用户完成才能进行抢购;利用setnx设置解决安全性问题,缺点是性能过低。

 

 

第五轮面试

1.死信、延迟、重试队列

死信、延迟、重试队列

#死信队列

DLQ(Deal Letter Queue),死信队列。当一个消息在队列中变成死信之后,他能被重新发送到 DLQ 中,与 DLQ 绑定到队列就是死信队列。

#什么情况下需要死信队列

·消息被拒绝

·消息过期

·队列达到最大长度

  

生产者生产一条消息,存储到普通队列中;设置队列的过期时间为 10 秒,在 10 秒内没有消费者消费消息,那么判定消息过期;此时如果设置了死信队列,过期消息被丢给死信队列交换机,然后被存储在死信队列中。

#延迟队列

顾名思义就是延迟执行消息,比如我们可以增加一个队列并设置其超时时间为 10 秒并且不设置任何消费者,等到消息超时,我们可以将消息放入死信队列,让消费者监听这个死信队列就达到了延迟队列的效果。

#重试队列

重试的消息在延迟的某个时间点(业务可设置)后,再次投递给消费者。而如果一直这样重复消费都持续失败到一定次数,就会投递到死信队列,最后需要进行人工干预。

 

  1. 双亲委派模型

双亲委派模型

#类加载器

#加载类的开放性

类加载器(ClassLoader)是 Java 语言的一项创新,也是 Java 流行的一个重要原因。在类加载的第一阶段加载过程中,需要通过一个类的全限定名来获取定义此类的二进制字节流,完成这个动作的代码块就是 类加载器。这一动作是放在 Java 虚拟机外部去实现的,以便让应用程序自己决定如何获取所需的类。

虚拟机规范并没有指明二进制字节流要从一个 Class 文件获取,或者说根本没有指明从哪里获取、怎样获取。这种开放使得 Java 在很多领域得到充分运用。

#类加载器与类的唯一性

类加载器虽然只用于实现类的加载动作,但是对于任意一个类,都需要由加载它的类加载器和这个类本身共同确立其在 Java 虚拟机中的 唯一性。通俗的说,JVM 中两个类是否相等,首先就必须是同一个类加载器加载的,否则,即使这两个类来源于同一个 Class 文件,被同一个虚拟机加载,只要类加载器不同,那么这两个类必定是不相等的。

这里的相等,包括代表类的 Class 对象的 equals() 方法、isAssignableFrom() 方法、isInstance() 方法的返回结果,也包括使用 instanceof 关键字做对象所属关系判定等情况。

 

 

#双亲委派模型

类加载器种类

 Java 虚拟机的角度来说,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用 C++ 语言实现(HotSpot 虚拟机中),是虚拟机自身的一部分;另一种就是所有其他的类加载器,这些类加载器都有 Java 语言实现,独立于虚拟机外部,并且全部继承自 java.lang.ClassLoader

从开发者的角度,类加载器可以细分为:

  • 启动(Bootstrap)类加载器:负责将 Java_Home/lib 下面的类库加载到内存中(比如 rt.jar)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。
  • 标准扩展(Extension)类加载器:是由 Sun  ExtClassLoadersun.misc.Launcher$ExtClassLoader 实现的。它负责将 Java_Home /lib/ext 或者由系统变量 java.ext.dir 指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。
  • 应用程序(Application)类加载器:是由 Sun  AppClassLoadersun.misc.Launcher$AppClassLoader 实现的。它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。开发者可以直接使用系统类加载器。由于这个类加载器是 ClassLoader 中的 getSystemClassLoader() 方法的返回值,因此一般称为系统(System)加载器。

除此之外,还有自定义的类加载器,它们之间的层次关系被称为类加载器的 双亲委派模型。该模型要求除了顶层的启动类加载器外,其余的类加载器都应该有自己的父类加载器,而这种父子关系一般通过组合(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)的出

 

  1. zookeeper假死脑裂

Zookeeper 假死脑裂

该问题就是服务集群因为网络震荡导致的多主多从问题,解决方案就是设置服务切换的超时时间,但也同时会导致无法达到高可用的要求。

 

  1. MySQL优化

MySQL 优化

  • 表关联查询时务必遵循 小表驱动大表 原则;
  • 使用查询语句 where 条件时,不允许出现 函数,否则索引会失效;
  • 使用单表查询时,相同字段尽量不要用 OR,因为可能导致索引失效,可以使用 UNION 替代;
  • LIKE 语句不允许使用 % 开头,否则索引会失效;
  • 组合索引一定要遵循 从左到右 原则,否则索引会失效;
  • 索引不宜过多,根据实际情况决定,尽量不要超过 10 个;
  • 每张表都必须有 主键,达到加快查询效率的目的;
  • 分表,可根据业务字段尾数中的个位或十位或百位(以此类推)做表名达到分表的目的;
  • 分库,可根据业务字段尾数中的个位或十位或百位(以此类推)做库名达到分库的目的;
  • 表分区,类似于硬盘分区,可以将某个时间段的数据放在分区里,加快查询速度,可以配合 分表 + 表分区 结合使用;

#神器 EXPLAIN 语句

EXPLAIN 显示了 MySQL 如何使用索引来处理 SELECT 语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句。

使用方法,在 SELECT 语句前加上 EXPLAIN 即可,如:

EXPLAIN SELECT * FROM tb_item WHERE cid IN (SELECT id FROM tb_item_cat)

 

    • id SELECT 识别符。这是 SELECT 的查询序列号
    • select_type SELECT类型,可以为以下任何一种
    • table 输出的行所引用的表
    • partitions 表分区
    • type 联接类型。下面给出各种联接类型,按照 从最佳类型到最坏类型 进行排序

system: 表仅有一行(=系统表)。这是 const 联接类型的一个特例。

possible_keys

 指出 MySQL 能使用哪个索引在该表中找到行

    • key 显示 MySQL 实际决定使用的键(索引)。如果没有选择索引, 键是 NULL
    • key_len 显示 MySQL 决定使用的键长度。如果键是 NULL, 则长度为 NULL
    • ref 显示使用哪个列或常数与 key 一起从表中选择行。
    • rows 显示 MySQL 认为它执行查询时必须检查的行数。多行之间的数据相乘可以估算要处理的行数。
    • filtered 显示了通过条件过滤出的行数的百分比估计值。
    • Extra 该列包含 MySQL 解决查询的详细信息

 

  1. JDK8新特性

JDK8 新特性

概述

以下列出两点重要特性:

  • Lambda 表达式(匿名函数)
  • Stream 多线程并行数据处理(重要)
  • 接口的默认方法只需要使用 default 关键字即可,这个特征又叫做 扩展方法
  • Lambda 表达式
  • Functional 接口 函数式接口 是指仅仅只包含一个抽象方法的接口,每一个该类型的 Lambda 表达式都会被匹配到这个抽象方法。你只需要给你的接口添加 @FunctionalInterface 注解
  • 使用 :: 双冒号关键字来传递方法(静态方法和非静态方法)
  • Predicate 接口和 Lambda 表达式
  • Function 接口
  • Supplier 接口,返回一个任意范型的值,和 Function 接口不同的是该接口 没有任何参数
  • Consumer 接口,接收一个任意范型的值,和 Function 接口不同的是该接口 没有任何值
  • Optional

新特性

小栗子

package com.funtl.jdk8.feature.lambda;

import java.util.Arrays;import java.util.List;import java.util.stream.Collectors;

/**

 * Lambda 基本用法

 *

Title: BaseLambda

 *

Description:

 *

 * @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 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 acgs = Arrays.asList(array);

 

        // Stream 去重复

        acgs = acgs.stream().distinct().collect(Collectors.toList());

 

        // 打印

        acgs.forEach(System.out::println);

    }}

 

  1. JavaScript this 关键字

JavaScript this 关键字

 

概述

任何执行 JavaScript 的环境称之为 执行上下文,默认情况下,执行上下文是全局的,这意味着如果代码作为简单函数调用的一部分执行,则该 this 变量将引用 全局对象 。在浏览器的情况下,全局对象是 windows 对象。但在 NodeJS 环境中,this 值是一个特殊的 global 对象。

简单函数

  // 案例 1,简单函数,浏览器下的 this -> windowNodeJS 下的 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 UserJavaScript 会在 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

  • call:将 this 指向目标对象
  • apply:将 this 指向目标对象,在参数形式为数组时使用该方法
  • bind:将 this 指向传递的第一个参数并返回一个新方法

    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("案例 6call 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()

 

  1. token和session

tokensession

什么是token?

token是服务端生成的一串字符串,目的是作为客户端进行请求的一个令牌。当第一次登录后,服务器生成一个token(一串字符串),并将此token返回给客户端,此后页面接收到请求后,只需要找到token即可获取信息,无需再输入登录名和密码。

token一般用于验证表明身份的数据或是别的口令数据;token可以用url传参,也可以是post提交,也可以夹在httpheader中。

什么是session?

session从字面上讲就是会话。

为了让服务器知道当前请求是从哪里发过来的,所以服务器给每个客户端分配了不同的身份标识”(类似id),之后客户端每次发送请求都携带上这个标识(id),这样做,服务端就可以知道是谁发送的请求。

session和token的区别:

服务器在使用session把用户信息临时保存在服务器上,用户离开网站后session就会被销毁。

session有一个缺陷:如果web服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时候session会丢失。

token最大特点就是支持跨平台操作,不论是在App还是在PC端,token都可以保留。

 

8.微服务

1.API管理

原理

SpringMVCRequestMappingHandlerMapping是比较重要的一个角色,它决定了每个URL分发至哪个Controller

Spring Boot加载过程如下,所以我们可以通过自定义WebMvcRegistrationsAdapter来改写RequestMappingHandlerMapping

 

 

  1. 服务熔断

Hystrix的熔断

Netflix’ Hystrix是第一个专门用于熔断的服务中间件。当它在2012年向公众发布,以提供对延迟和失败有更大容忍度的微服务架构时,Netflix已经在内部广泛使用了一年多的时间了。根据这个项目的描述,Hystrix一直是Netflix服务中间件的基本组成部分之一,直到2018年底进入维护模式,这标志着“[关注点]转向更适应应用程序实时性能的实现,而不是预先配置的设置。

Hystrix是一个Java库,开发人员可以使用它用熔断逻辑封装服务调用。它基于阈值,可以立即判定调用失败并执行回滚逻辑,具体参考第一部分。除了提供超时和并发限制之外,它还可以向监视工具发布度量metrics。最后,当与Archaius库一起使用时,它还可以支持动态配置更改。

服务网格中的熔断

Istio

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,里面详细讲解了服务追踪系统的实现原理。)

要理解服务追踪的原理,首先必须搞懂一些基本概念:traceIdspanIdannonation 等。Dapper 这篇论文讲得比较清楚,但对初学者来说理解起来可能有点困难,美团的 MTrace 的原理介绍理解起来相对容易一些,下面我就以 MTrace 为例,给你详细讲述服务追踪系统的实现原理。虽然原理有些晦涩,但却是你必须掌握的,只有理解了服务追踪的基本概念,才能更好地将其实现出来。

4.配置中心地方

配置实时生效:

传统的静态配置方式要想修改某个配置只能修改之后重新发布应用,要实现动态性,可以选择使用数据库,通过定时轮询访问数据库来感知配置的变化。轮询频率低感知配置变化的延时就长,轮询频率高,感知配置变化的延时就短,但比较损耗性能,需要在实时性和性能之间做折中。配置中心专门针对这个业务场景,兼顾实时性和一致性来管理动态配置。

配置管理流程:

配置的权限管控、灰度发布、版本管理、格式检验和安全配置等一系列的配置管理相关的特性也是配置中心不可获取的一部分。

开源配置中心基本介绍

目前市面上用的比较多的配置中心有:(按开源时间排序)

Disconf

20147月百度开源的配置管理中心,同样具备配置的管理能力,不过目前已经不维护了,最近的一次提交是两年前了。

Spring Cloud Config

20149月开源,Spring Cloud 生态组件,可以和Spring Cloud体系无缝整合。

Apollo

20165月,携程开源的配置管理中心,具备规范的权限、流程治理等特性。

Nacos

20186月,阿里开源的配置中心,也可以做DNSRPC的服务发现。

配置中心核心概念的对比

由于Disconf不再维护,下面对比一下Spring Cloud ConfigApolloNacos
Spring Cloud ConfigApolloNacos在配置管理领域的概念基本相同,但是也存在一些不同的点,使用配置的过程中会涉及到一些比较重要的概念。

应用

应用是客户端系统的基本单位,Spring Cloud Config 将应用名称和对应Git中的文件名称关联起来了,这样可以起到多个应用配置相互隔离的作用。Apollo的配置都是在某个应用下面的(除了公共配置),也起到了多个应用配置相互隔离的作用。Nacos的应用概念比较弱,只有一个用于区分配置的额外属性,不过可以使用 Group 来做应用字段,可以起到隔离作用。

集群

不同的环境可以搭建不同的集群,这样可以起到物理隔离的作用,Spring Cloud ConfigApolloNacos都支持多个集群。

Label Profile & 环境 & 命名空间

Spring Cloud Config可以使用LabelProfile来做逻辑隔离,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权限控制可以分为AdminWriteRead权限,权限管理比较完善。

Apollo通过项目的维度来对配置进行权限管理,一个项目的owner可以授权给其他用户配置的修改发布权限。

Nacos目前看还不具备权限管理能力。

版本管理&回滚

当配置变更不符合预期的时候,需要根据配置的发布版本进行回滚。Spring Cloud ConfigApolloNacos都具备配置的版本管理和回滚能力,可以在控制台上查看配置的变更情况或进行回滚操作。Spring Cloud Config通过Git来做版本管理,更方便些。

配置格式校验

应用的配置数据存储在配置中心一般都会以一种配置格式存储,比如PropertiesJsonYaml等,如果配置格式错误,会导致客户端解析配置失败引起生产故障,配置中心对配置的格式校验能够有效防止人为错误操作的发生,是配置中心核心功能中的刚需。
Spring Cloud Config使用Git,目前还不支持格式检验,格式的正确性依赖研发人员自己。
ApolloNacos都会对配置格式的正确性进行检验,可以有效防止人为错误。

监听查询

当排查问题或者进行统计的时候,需要知道一个配置被哪些应用实例使用到,以及一个实例使用到了哪些配置。
Spring Cloud Config使用Spring Cloud Bus推送配置变更,Spring Cloud Bus兼容 RabbitMQKafka等,支持查询订阅TopicConsumer的订阅关系。
Apollo可以通过灰度实例列表查看监听配置的实例列表,但实例监听的配置(Apollo称为命名空间)目前还没有展示出来。

Nacos可以查看监听配置的实例,也可以查看实例监听的配置情况。

基本上,这三个产品都具备监听查询能力,在我们自己的使用过程中,Nacos使用起来相对简单,易用性相对更好些。

多环境

在实际生产中,配置中心常常需要涉及多环境或者多集群,业务在开发的时候可以将开发环境和生产环境分开,或者根据不同的业务线存在多个生产环境。如果各个环境之间的相互影响比较小(开发环境影响到生产环境稳定性),配置中心可以通过逻辑隔离的方式支持多环境。

Spring Cloud Config支持Profile的方式隔离多个环境,通过在Git上配置多个Profile的配置文件,客户端启动时指定Profile就可以访问对应的配置文件。

Apollo也支持多环境,在控制台创建配置的时候就要指定配置所在的环境,客户端在启动的时候指定JVM参数ENV来访问对应环境的配置文件。

Nacos通过命名空间来支持多环境,每个命名空间的配置相互隔离,客户端指定想要访问的命名空间就可以达到逻辑隔离的作用。

多集群

当对稳定性要求比较高,不允许各个环境相互影响的时候,需要将多个环境通过多集群的方式进行物理隔离。

Spring Cloud Config可以通过搭建多套Config ServerGit使用同一个Git的多个仓库,来实现物理隔离。

Apollo可以搭建多套集群,Apollo的控制台和数据更新推送服务分开部署,控制台部署一套就可以管控多个集群。

Nacos控制台和后端配置服务是部署在一起的,可以通过不同的域名切换来支持多集群。

配置实时推送的对比

当配置变更的时候,配置中心需要将配置实时推送到应用客户端。

NacosApollo配置推送都是基于HTTP长轮询,客户端和配置中心建立HTTP长联接,当配置变更的的时候,配置中心把配置推送到客户端。

 

Spring Cloud Config原生不支持配置的实时推送,需要依赖GitWebHookSpring Cloud Bus和客户端/bus/refresh端点:

  •  

基于GitWebHook,配置变更触发serverrefresh

  •  
  •  

Server端接收到请求并发送给Spring Cloud Bus

  •  
  •  

Spring Cloud Bus接到消息并通知给客户端

  •  
  •  

客户端接收到通知,请求Server端获取最新配置

  •  

 

 

5.API网关

1API网关介绍

API网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。

API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。

 
2、融入架构
 

 

API网关负责服务请求路由、组合及协议转换。客户端的所有请求都首先经过API网关,然后由它将请求路由到合适的微服务。API网管经常会通过调用多个微服务并合并结果来处理一个请求。它可以在Web协议(如HTTPWebSocket)与内部使用的非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算法。这种模式的一个重大缺陷在于,客户端和服务注册表是一一对应的,必须为服务客户端用到的每一种编程语言和框架实现客户端服务发现逻辑。

服务器端服务发现模式

下图展示了这种模式的架构 
  
客户端通过负载均衡器向一个服务发送请求,这个负载均衡器会查询服务注册表,并将请求路由到可用的服务实例上。通过客户端的服务发现,服务实例在服务注册表上被注册和注销

AWSELBElastic Load Blancer)就是一个服务器端服务发现路由器。一个ELB通常被用来均衡来自互联网的外部流量,也可以用ELB去均衡流向VPCVirtual Private Cloud)的流量。一个客户端通过ELB发送请求(HTTPTCP)时,使用的是DNSELB会均衡这些注册的EC2实例或ECSEC2 Container Service)容器的流量。没有另外的服务注册表,EC2实例和ECS容器也只会在ELB上注册。

HTTP服务器和类似NginxNginx Plus的负载均衡器也可以被用做服务器端服务发现负载均衡器。例如,Consul Template可以用来动态配置Nginx的反向代理。

Consul Template定期从存储在Consul服务注册表的数据中,生成任意的配置文件。每当文件变化时,会运行一个shell命令。比如,Consul Template可以生成一个配置反向代理的nginx.conf文件,然后运行一个命令告诉Nginx去重新加载配置。还有一个更复杂的实现,通过HTTP APIDNS去动态地重新配置Nginx Plus

有些部署环境,比如KubernetesMarathon会在集群中的每个host上运行一个代理。这个代理承担了服务器端服务发现负载均衡器的角色。为了向一个服务发送一个请求,一个客户端使用hostIP地址和服务分配的端口,通过代理路由这个请求。这个代理会直接将请求发送到集群上可用的服务实例。

服务器端服务发现模式也是优势和缺陷并存。最大的好处在于服务发现的细节被从客户端中抽象出来,客户端只需要向负载均衡器发送请求,不需要为服务客户端使用的每一种语言和框架,实现服务发现逻辑;另外,这种模式也有一些问题,除非这个负载均衡器是由部署环境提供的,又是另一个高需要启动和管理的可用的系统组件。

服务注册表(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服务器。

其它服务注册的例子包括:

  • Etcd:一个高可用,分布式,一致的key-value存储,用来共享配置和服务发现。KubernetesCloudfoundry都使用了etcd
  • Consul:一个发现和配置服务的工具。客户端可以利用它提供的API,注册和发现服务。Consul可以执行监控检测来实现服务的高可用;
  • Apache Zookeeper:一个常用的,为分布式应用设计的高可用协调服务,最开始ZookeeperHadoop的子项目,现在已经顶级项目了。

一些系统,比如KubernetesMarathonAWS没有一个明确的服务注册组件,这项功能是内置在基础设置中的。

下面我们来看看服务实例如何在注册表中注册。

服务注册(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支持etcdConsul等服务注册。

另一个service registrar的例子是NetflixOSS Prana。主要用于非JVM语言编写的服务,它是一个和服务实例配合的『双轮』应用。Prana会在Netflix Eureka上注册和注销实例。

service registrar是一个部署环境的内置组件,由Autoscaling Group创建的EC2实例可以被ELB自动注册。Kubernetes服务也可以自动注册。

third-party registration模式主要的优势在于解耦了服务和服务注册表。不需要为每个语言和框架都实现服务注册逻辑。服务实例注册由一个专用的服务集中实现。缺点是除了被内置到部署环境中,它本身也是一个高可用的系统组件,需要被启动和管理。

 

7.分布式服务接口的幂等性如何设计

面试官心理分析

从这个问题开始,面试官就已经进入了实际的生产问题的面试了。

一个分布式系统中的某个接口,该如何保证幂等性?这个事儿其实是你做分布式系统的时候必须要考虑的一个生产环境的技术问题。啥意思呢?

你看,假如你有个服务提供一些接口供外部调用,这个服务部署在了 5 台机器上,接着有个接口就是付款接口。然后人家用户在前端上操作的时候,不知道为啥,总之就是一个订单不小心发起了两次支付请求,然后这俩请求分散在了这个服务部署的不同的机器上,好了,结果一个订单扣款扣两次。

或者是订单系统调用支付系统进行支付,结果不小心因为网络超时了,然后订单系统走了前面我们看到的那个重试机制,咔嚓给你重试了一把,好,支付系统收到一个支付请求两次,而且因为负载均衡算法落在了不同的机器上,尴尬了。。。

所以你肯定得知道这事儿,否则你做出来的分布式系统恐怕容易埋坑。

面试题剖析

这个不是技术问题,这个没有通用的一个方法,这个应该结合业务来保证幂等性。

所谓幂等性,就是说一个接口,多次发起同一个请求,你这个接口得保证结果是准确的,比如不能多扣款、不能多插入一条数据、不能将统计值多加了 1。这就是幂等性。

其实保证幂等性主要是三点:

  • 对于每个请求必须有一个唯一的标识,举个栗子:订单支付请求,肯定得包含订单 id,一个订单 id 最多支付一次,对吧。
  • 每次处理完请求之后,必须有一个记录标识这个请求处理过了。常见的方案是在 mysql 中记录个状态啥的,比如支付之前记录一条这个订单的支付流水。
  • 每次接收请求需要进行判断,判断之前是否处理过。比如说,如果有一个订单已经支付了,就已经有了一条支付流水,那么如果重复发送这个请求,则此时先插入支付流水,orderId 已经存在了,唯一键约束生效,报错插入不进去的。然后你就不用再扣款了。

实际运作过程中,你要结合自己的业务来,比如说利用 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、黑名单

 

 

 

9.zookeeper 都有哪些使用场景?

大致来说,zookeeper 的使用场景如下,我就举几个简单的,大家能说几个就好了:

  • 分布式协调
  • 分布式锁
  • 元数据/配置信息管理
  • HA高可用性

 

分布式协调

  这个其实是 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:底层数据库的压力分摊

如果系统的性能压力出现在数据库,那我们就可以读写分离、分库分表等方案进行解决。

11.spring bootspring cloud

1服务器断电导致虚拟机数据丢失的恢复方法

这道题比较偏运维,并不是我们开发的职能范围。对于3年以内的开发算是超纲的面试题了,这种题目的回答最好说自己没有权限操作服务器,或者说是组长负责,技术经理负责。强行回答会陷进面试官的坑里

有兴趣可以看这篇文章:https://www.jb51.net/article/133246.htm

2 springbootspringcloud实现原理

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,更有效率,快捷。

项目的构建流程

项目构建流程

  项目立项

  技术选型和项目框架搭建

  需求分析

  开发阶段

    UI界面设计

    编码开发

后端开发(整理完需求即可开始接口开发)

   前端开发(依赖于UI界面设计和后端接口)

测试(完整的测试由测试==>修改==>回归测试组成)

功能测试

性能测试

上线

  

 

 

4 redis项目应用,在哪块,存哪些数据

redis的使用场景很多,回答是不但需要答出在哪里使用,最好要答出为什么这么使用

1) 登录,权限控制:session 数据的缓存

  1. 解决跨域,
  2. 多台服务器应用,实现session数据一致性

具体步骤:   

用户进行登录时,提交的登录表单,放入request

    服务器端校验用户名和密码;

    通过后将用户信息存储到Redis中,在数据库中的keysession_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的特点(必须了解):

  • 内存数据库,速度快,也支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
  • Redis不仅仅支持简单的key-value类型的数据,同时还提供listsetzsethash等数据结构的存储。
  • Redis支持数据的备份,即master-slave模式的数据备份。
  • 支持事务

如何解决高并发请求

高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。

高并发相关常用的一些指标:

响应时间:系统对请求做出响应的时间。例如系统处理一个HTTP请求需要200ms,这个200ms就是系统的响应时间。

吞吐量:单位时间内处理的请求数量。

QPS:每秒响应请求数。在互联网领域,这个指标和吞吐量区分的没有这么明显。

并发用户数:同时承载正常使用系统功能的用户数量。例如一个即时通讯系统,同时在线量一定程度上代表了系统的并发用户数。

互联网分布式架构设计,提高系统并发能力的方式,方法论上主要有两种:垂直扩展(Scale Up)与水平扩展(Scale Out)。

垂直扩展:提升单机处理能力。垂直扩展的方式又有两种:

1增强单机硬件性能,例如:增加CPU核数如32核,升级更好的网卡如万兆,升级更好的硬盘如SSD,扩充硬盘容量如2T,扩充系统内存如128G

2提升单机架构性能,例如:使用Cache来减少IO次数,使用异步来增加单服务吞吐量,使用无锁数据结构来减少响应时间;

不管是提升单机硬件性能,还是提升单机架构性能,都有一个致命的不足:单机性能总是有极限的。所以互联网分布式架构设计高并发终极解决方案还是水平扩展。

水平扩展:只要增加服务器数量,就能线性扩充系统性能。水平扩展对系统架构设计是有要求的,如何在架构各层进行可水平扩展的设计,以及互联网公司架构各层常见的水平扩展实践

我们公司最早是一台服务器28Gb,当用户交互开始卡顿时升级到了416Gb,后面再次升级成了432GB。当用户量上来后,开始使用集群。

这里就是先做垂直扩展,后做水平扩展

项目初期为了快速迭代开发、推荐业务和成本看了,一般选用垂直扩展。当项目起来,用户量上来盈利了,会选用水平扩展来支撑日益复杂的需求

项目中那一块业务用到多线程了?

定时短信、站内信的发送,涉及上万用户,若单线程的话耗时会很长,多线程能大大缩短发送时间

定时操作列表记录(记录之间操作必须独立)

导入大批量数据

项目已经上线,出现bug怎么解决,如果宕机了怎么办

线上bug一般分为紧急和非紧急:

对于紧急的bug会立即组织测试和开发进行排查,若是代码问题实时修复,修复后测试没问题通知上线,上线后再测试和跟进

对于非紧急的bug会提交到bug管理工具(如禅道),统一时间进行排查和修复.修复测试没问题在指定时间进行上线

由于开发人员和测试人员有限,对于bug修复必须分情况和优先级进行修复,毕竟开发和测试有其他任务.

我们应该部署多少台服务器,每台服务器承受的压力是多少

部署多少台服务器根据项目实际情况而定,一般用户在10万以下并不会使用到集群,项目功能简单也不会使用到分布式服务.

公司项目初期为了快速迭代开发一般一个应用即可,这时部署一台服务器即可.从成本和效率上来说是最好的

项目中期的时候,随着功能复杂起来,会对功能模块进行拆分,这时部署会根据模块服务数和用户数来部署

目前我们公司的服务器情况:

商家数8000+,日活商家数1200+,用户数90w+,日活2w+:

PC web2台服务器,H5 web3台服务器,后台管理1台服务器,核心服务(处理各种定时任务)部署了一台服务器,上传服务器2,短信服务器2,支付服务器2,图片服务器采用的是阿里云的OSS,数据库采用的是阿里云的RDS-mysql,缓存采用的是阿里云的redis

服务器承受的压力有很多因素:服务器的配置,应用的多少和性能都有关系,我们公司的服务器配置是816G,这种配置服务器能承受并发能过2000以上

实际开发中一个项目开发到什么样才能上线

正常上线必须通过产品和测试,产品需要确定需求中的功能是否已经实现,测试需要确定现有功能是否已经没有功能性问题,只有通过他们的验收项目或者新需求才能上线.

11 RabbitMQ能承载多少的高并发

RabbitMQ能承载多少高并发于服务器性能有关,测试48G的服务器上RabbitMQ的并发能破1000

这种题目只要说听测试同学说这个样子,毕竟不是所有人都会进行性能测试的.

12 数据库存储过程,项目中有用吗,处理什么业务

MySQL 5.0 版本开始支持存储过程。

存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。

存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。

存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。

存储过程就是具有名字的一段代码,用来完成一个特定的功能。

创建的存储过程保存在数据库的数据字典中。

优点

存储过程可封装,并隐藏复杂的商业逻辑。

存储过程可以回传值,并可以接受参数。

存储过程无法使用 SELECT 指令来运行,因为它是子程序,与查看表,数据表或用户定义函数不同。

存储过程可以用在数据检验,强制实行商业逻辑等。

缺点

存储过程,往往定制化于特定的数据库上,因为支持的编程语言不同。当切换到其他厂商的数据库系统时,需要重写原有的存储过程。

存储过程的性能调校与撰写,受限于各种数据库系统。

存储过程总的来说比较鸡肋:

受限于数据库和编程语言,导致各个公司使用时有顾虑,之后的迁移和转型成本会很大

编写要求不像普通sql方便,没有使用熟悉的编程语言开发效率高

目前我待过的几个项目中均没有使用到存储过程

13 MySQL的优化、百万千万级数据处理有什么方法

mysql优化:

目前用到过的

实际开发中禁止使用’select *’

当知道结果只有一行数据时使用 LIMIT 1,这样一来,MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查少下一条符合记录的数据。

为常用的搜索字段建索引

4永远为每张表设置一个ID做为其主键,而且最好的是一个INT型的(推荐使用UNSIGNED),并设置上自动增加的AUTO_INCREMENT标志。

5 like’%‘在前面用不到索引,尽可能将’%‘放在后面,进行有模糊查询

6 mysql 不支持函数转换,所以字段前面不能加函数,否则这将用不到索引

字段类型转换导致不用索引,如字符串类型的不用引号,数字类型的用引号等,这有可能会用不到索引导致全表扫描

当数据量大的时候需要分库分表

网上推荐的还有:

Join表的时候使用相当类型的例,并将其索引:当两个表中Join的字段是被建过索引时,MySQL内部会启动为你优化JoinSQL语句的机制

千万不要 ORDER BY RAND(),这样使用只会让你的数据库的性能呈指数级的下降

使用 ENUM 而不是 VARCHAR

4 or 的查询尽量用 union 代替(Innodb

应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描

应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描

7应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描

8 in 和 not in 也要慎用,否则会导致全表扫描

 

当然要分析语句是否要优化一定得会使用Explain关键字,从 PROCEDURE ANALYSE() 取得建议

 

千、百万级数据处理方法:

当数据达到千百万级别是,查询的效率是个很大的问题,这时写完sql语句都应该使用explain来查看下语句的查询信息,运行下语句看看使用的时间

当效率很低的时候就必须进行优化了:

从语句的性能进行优化 -- 以上的优化手段都可以用

从表结构进行优化,常见的有

A)对表字段拆分,比如商品表,商品的名称,主图,规格等不变的字段放到基础信息表中,商品的实时信息放到附属表中;

B) 对大表进行按时间拆分表,比如用户表,对指定时间内(60天)没有登录过的用户放到一张用户冻结表,其他的放在用户表,当冻结表中的用户重新登录后再移至用户表中

从硬件上进行升级

前面两种是减少语句的扫描量(查询量),后面一种是提高扫描(查询)的性能和效率

14 Oracle出现索引失效怎么办

1. 选用适合的Oracle优化器

2、‍重建索引:‍alter index 索引名 rebuild online

3、强制索引:给该语句加上hint后,强制其使用指定索引

15 jvm参数调优

JVM 中最大堆大小有三方面限制:

a.相关操作系统的数据模型(32-bt还是64-bit)限制;

b.系统的可用虚拟内存限制;

c.系统的可用物理内存限制。

32位系统下,一般限制在1.5G~2G64为操作系统对内存无限制。

常见配置汇总

堆设置

-Xms:初始堆大小

-Xmx:最大堆大小

-Xmn:年轻代大小

-Xss:每个线程的虚拟机栈大小

-XX:NewSize=n:设置年轻代大小

-XX:NewRatio=n:设置年轻代和年老代的比值。如:3,表示年轻代与年老代比值为13,年轻代占整个年轻代年老代和的1/4

-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示EdenSurvivor=32,一个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,则年轻代与年老代所占比值为14,年轻代占整个堆栈的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 整个系统从前端到数据库再到前端,代码优化,结构优化,通常考虑哪些问题

请求的耗时

请求的个数

查询的个数

查询的耗时

代码运行的耗时

代码的性能

代码的耦合性

代码的可读性

总之:优化一定得从以下几个方面考虑

性能:如代码的耗时,请求响应速度,请求的个数,查询的个数,查询的耗时

可维护性:如代码的耦合性,可读性,可维护性

19 怎么防止接口暴露别人用工具恶意刷接口

常见的方式:

图形验证码,这常用于短信接口的防护

限定请求次数,常见于秒杀,抢购等功能中

流程条件限制,如满足一定条件,注册用户,获取权限等

4 ip地址限定,java中一般写拦截器对统一ip进行判断,同一ip指定时间内只能访问指定次数

服务器接口验证:常见于一些需要付费的接口,这类接口需要内部的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级别的信息。一般更新数据库数据前后需要打日志,敏感操作节点也需要打日志.这些日志都会写到日志文件中保存起来,一般的日志文件保存715.

跟踪线上问题:

先确定问题是在哪个页面或者哪个功能中,然后查看功能对应的日志文件,看看是否有Error信息或者Exception信息

若没有异常信息说明很可能是代码逻辑的问题,查看功能日志点看看日志情况,一般是能定位问题点

3 若从日志中定位不出来只能是复盘功能代码

日志的存储:

单服务器上日志一般存在指定的目录下,可在日志配置中定义

分布式/集群:可以存在各种服务器上,也可以使用日志服务器统一管理日志文件

25 和测试如何交互的

测试的职责是找出项目应用中存在的问题,及时提交给开发修复.在上线前将问题尽可能的找出处理掉.现在很多开发觉得测试和他们是对立面,经常和测试针锋相对,推脱问题.其实测试和开发都是为了将项目功能尽可能的完善,交付给用户使用,这是他们的共同目的.

和测试的交互应该注重客观事实,根据测试提交的问题排查是什么原因造成的,然后有问题的解决掉,没问题的反馈给测试重新测试.重点是和测试多沟通,了解彼此的想法,不能造成误解,影响工作.

至少我现在公司开发和测试很和谐,每天测试工作时遇到需要紧急需马上解决的问题会马上和开发沟通,是否真正有问题由开发排查后反馈解决.非紧急的问题会提交到禅道上,由开发自己去看和解决自己的问题.测试每天定点看禅道的bug解决率,并提醒开发前去解决bug

27 BUG处理的过程是怎么样

问题的反馈测试测试出来或者用户反馈过来

问题的确定开发拿到问题后现在测试服中重现问题,无法重现的通过查看日志,复盘代码来定位问题

问题的解决一般小问题会在主干上修复并测试上线,大问题会开分支专门修复问题测试没问题后合并再测试上线

通知问题解决上线后通知相关的部门和人员修复结果(做到事事有结果)

26 项目如何部署

java项目部署目前一般部署在tomcat上,springboot出现后直接将项目打成jar包进运行。

部署前需要对环境进行确认:确认项目应用所需的环境是否正确,如系统环境,java版本,数据库版本,系统的硬件配置

对项目的配置文件进行确认:有些项目的配置文件在测试时需要修改,这时上线的时候需要改回去,如果没有check,可能会使用测试配置上线,这样就好导致上线出现问题

对数据库表字段进行确认:防止因为新增的表或字段未添加或名称有出入导致上线后报错

对上线时间的确认和通知

上线时对以前线上的代码进行备份:确保上线失败时可以回退

上线后:必须对所上线的功能进行再次测试,防止有问题

28 你们商城开发中都遇到过什bug

未做分布式锁时经常会出现并发和重复请求的数据:添加分布式锁

秒杀抢购被用户摸到接口,使用工具重复提交抢购:使用拦截器对同一ip同一接口进行请求间隔限制

一个用户多地登录导致数据异常:限制用户只能一地登录

数据库被清空:好在数据库数据每小时都有做备份,吧备份数据导回线上数据库,但还是损失了备份点之后的数据

系统达到瓶颈:升级硬件配置,使用集群

29 订单你们是怎么做的实现思路是什么

目前我碰到的有两种实现方式:

用户购买一件商品就生成一条订单记录,商品的库存减一

在商品发布时生成库存量个token,每次用户购买商品就获取一个token,同时生成一条订单记录,token使用完后失效

第一种需要注意重复提交和并发问题

第二种适用于商品库存量不大的应用

30 支付你们怎么做的,如何和支付宝和微信对接的,怎么保证安全

微信和支付宝支付接口的对接都有相应的流程,他们都提供了详细的对接demo.这两种支付方式以及快钱支付方式的对接都基本一致.

原理:

用户发起支付请求,应用服务端将支付的金额,支付的原因以及应用生成的唯一商户订单号使用支付接口方提供的加签方式进行加签后,生产预支付记录,同事调用支付接口进行支付.支付提交后有两种获取回执相应的方式:同步和异步.同步是用户点击支付完成,由服务端像支付接口方服务器发起查询,对查询结果进行校验操作.异步是支付接口方接受到支付打款后向我们应用指定接口发生支付成功的请求,我们对请求进行解析校验操作.无论异步还是同步都会返回我们生成的唯一商户订单号作为回执.操作成功后,根据商户订单进行更新记录

 

安全性这几种支付方式在提交支付是都会有加签步,当对应支付完成会有他们服务器回调我们接口.这时我们对他们的请求参数用指定的密钥和解签方式进行解签,对解签结果进行校验.

31 假设有用户在手机端和PC端同时对购物车里的商品进行下单处理怎么办

 对购物车里的商品加锁,只有拿到锁的一方才能进行下单处理,另一方无法下单操作.

32 怎么保证用户登录之后,保证用户的数据一个安全性,保证用户的数据不会被第三方拦截

现在我们处理的方法:

1加密,一般是对称加密的方法.

信息使用特殊字符替换,如手机号:在服务端将手机号中间几位使用*替换掉

 

12.SVN

SVN是一个开放源代码的版本控制系统,通过采用分支管理系统的高效管理,简而言之就是用于多个人共同开发同一个项目,实现共享资源,实现最终集中式的管理。

基本操作

本地项目上传到SVN

项目名称 -> 右键 -> Team - > Share Project...

常用操作

commit

update

compare with ...

replace with ...

冲突处理

  • update -> 处理冲突代码 -> commit
  • 同步SVN时合并冲突代码 -> 标记为合并 -> commit
  • backup本地代码 -> replace with head -> 恢复本地代码 -> commit (尽量避免这种方式)

 

trunktagsbranches 概述

 

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

 

13.爬虫、单点登录、八级联动、调度

爬虫:

由于在我参与的那个项目中,出于市场营销的需要,为了获取最新的XXXX信息,我们用Python语言做了一个比较简单的网络爬虫,专门用来在新浪微博上爬取最近的热搜,给市场部的员工作为参考。

 

单点登录(SSO:

另外我们的项目因为分成了多个系统,为了解决cookie无法跨域导致的用户重复登录问题,我们使用了token技术来实现单点登录。

 

八级联动:

因为我参与的那个项目,在购物后填写收货地址时需要选择详细的地址,我们使用了下拉菜单的多级联动,也就是创建了多张表,每一张子表的副id都是父表的id,最后达到了八级联动。

 

调度:

为了解决项目中定时的公告发布与事务进行,我们使用了Quartz任务日程管理系统,比如准时发布全站公告和商城信息,以及在规定的时间运行某段代码。

 

14.shiro

在这个项目中,我们使用的是shiro安全框架管理用户登录以及资源权限的控制。

实现细节:用户在点击登录后,会将用户信息封装成一个token调用subject.login()方法提交认证,shirosecurityManager安全管理器会调用Authenticator执行认证,调用realmrealm类需要实现AuthorizingRealm接口重写认证和授权的方法)的token.getPrincipal()方法获取用户名以及密码,将密码与数据库中该用户的密码进行对比,如果验证成功,将返回 AuthenticationInfo 验证信息;此信息中包含了身份及凭证;如果验证失败将抛出相应的 AuthenticationException 实现。如果登录成功,会根据当前登录用户获取对应的菜单权限,在realm中进行用户的权限的授予。调用realmhasRole判断当前用户是否有这个角色权限。isPermittedshiro框架提供的判断是否拥有某个权限或所有权限,针对不同权限加载不同的菜单或者执行不同的操作权限。

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,并的到处理结果

将处理结果通过ajaxfastjsonwebService返回的json对象解析到前台并展示

17.反射报表

JAVA中的反射是运行中的程序检查自己和软件运行环境的能力,它可以根据它发现的进行改变。通俗的讲就是反射可以在运行时根据指定的类名获得类的信息。
 
首先我们先明确两个概念,静态编译和动态编译。
 
静态编译:在编译时确定类型,绑定对象,即通过。 动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多    态的应用,有以降低类之间的藕合性。   
 
我们可以明确的看出动态编译的好处,而反射就是运用了动态编译创建对象。
 
 
报表就是将项目中的信息以excel的形式导出来,共客户方财务或者我们的运维人员使用。
当然报表模块使用的也就是一个工具类Jcreporter,直接调用即可,像一般用于报表制作的还有poi。
mybatis分页:首先先在maven中导入jar包
     com.github.pagehelper
     pagehelper
     4.1.4
 
然后在spring.xml中配置
 
    
        
        
        
 
        
            
                
                    
                        
                            helperDialect=mysql
                            reasonable=true     
                        
                    
                
            
        
最后直接进行分页操作,创建一个返回值是pageinfo的接口
 //分页查询
    public PageInfo selectByPage(Integer pageNum, Integer pageSize) {
    
        //对下面的查询进行分页        PageHelper.startPage(pageNum,pageSize);
        List list = mapper.selectByPage();
        return new PageInfo(list,4);
    }

 

18.ELK+Kafka日志收集环境搭建

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)测试eshttp://192.168.115.158:9200 出现es的版本号说明配置环境成功。

2.搭建logstash
  (1)打开物料中logstash
  步骤:进入logstash的容器
  vi /usr/local/logstash-6.3.0/config/logstash.yml(修改连接esip)
  修改成:
  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环境常用的配置文件有哪些?

1mycat是什么?

国内最活跃的、性能最好的开源数据库分库分表中间件

  • 一个彻底开源的,面向企业应用开发的大数据库集群
  • 支持事务、ACID、可以替代MySQL的加强版数据库
  • 一个可以视为MySQL集群的企业级数据库,用来替代昂贵的Oracle集群
  • 一个融合内存缓存技术、NoSQL技术、HDFS大数据的新型SQL Server
  • 结合传统数据库和新型分布式数据仓库的新一代企业级数据库产品
  • 一个新颖的数据库中间件产品

2、分片规则:取模

    
        user_id
        mod-long
    
    
    3

配置说明:
| 标签属性 | 说明 |
| --------- | -------------------- |
| columns |
标识将要分片的表字段 |
| algorithm |
分片函数 |
| count |
分片数量 |

根据 id 进行十进制求模预算。

3、搭建mycat环境常用的配置文件

 

rule.xml

     
    
        id
        rang-mod-dm
    
    3

schema.xml

    
       
        rule="auto-sharding-rang-mod-order"/>
    
    
    writeType="0"   dbType="mysql"
        dbDriver="native" switchType="1"  slaveThreshold="100">
    select 1
    
        url="192.168.133.130:3306" user="root" password="123456">
    

server.xml

    
    0  
    0  
 
        2
       
         
    
    
        
        0
        
        
        
        
        
        
        
        
        0
        
            
        1
 
        
        1m
 
        
        1k
 
        0
 
        
        384m
 
 
        
        true
 
 
    
    
    
    
    
    
        123456
        dm_order
        
        
        
    

 

20.SpringBoot SpringCloud 之间关系?

SpringBoot:专注于快速方便的开发单个个体微服务(关注微观);
SpringCloud:关注全局的微服务协调治理框架,将SpringBoot开发的一个个单体微服务组合并管理起来(关注宏观);
SpringBoot可以离开SpringCloud独立使用,但是SpringCloud不可以离开SpringBoot,属于依赖关系。

21.权限管理

权限管理是系统的安全范畴,要求必须是合法的用户才可以访问系统(用户认证),且必须具有该资源的访问权限才可以访问该资源(授权)。

认证:对用户合法身份的校验,要求必须是合法的用户才可以访问系统。

授权:访问控制,必须具有该资源的访问权限才可以访问该资源。

 

权限模型:标准权限数据模型包括:用户、角色、权限(包括资源和权限)、用户角色关系、角色权限关系。

权限分配:通过UI界面方便给用户分配权限,对上边权限模型进行增、删、改、查操作。

权限控制:

         基于角色的权限控制:根据角色判断是否有操作权限,因为角色的变化性较高,如果角色修改需要修改控制代码,系统可扩展性不强。

         基于资源的权限控制:根据资源权限判断是否有操作权限,因为资源较为固定,如果角色修改或角色中权限修改不需要修改控制代码,使用此方法系统可维护性很强。建议使用。

 

权限管理的解决方案:

对于粗颗粒权限管理,建议在系统架构层面去解决,写系统架构级别统一代码(基础代码)。

         粗颗粒权限:比如对系统的url、菜单、jsp页面、页面上按钮、类方法进行权限管理,即对资源类型进行权限管理。

 

n           对于细颗粒权限管理:

         粗颗粒权限:比如用户id001的用户信息(资源实例)、类型为t01的商品信息(资源实例),对资源实例进行权限管理,理解对数据级别的权限管理。

         细颗粒权限管理是系统的业务逻辑,业务逻辑代码不方便抽取统一代码,建议在系统业务层进行处理。

 

基于url的权限管理(掌握):

         企业开发常用的方法,使用web应用中filter来实现,用户请求url,通过filter拦截,判断用户身份是否合法(用户认证),判断请求的地址是否是用户权限范围内的url(授权)

 

shiro

         shiro是一个权限管理框架,是apache下的开源项目。相比springsecurity框架更简单灵活,spring securityspring依赖较强。shiro可以实现web系统、c/s、分布式等系统权限管理。

 

shiro认证流程:(掌握)

         1subject(主体)请求认证,调用subject.login(token)

         2SecurityManager(安全管理器)执行认证

         3SecurityManager通过ModularRealmAuthenticator进行认证。

         4ModularRealmAuthenticatortoken传给realmrealm根据token中用户信息从数据库查询用户信息(包括身份和凭证)

    5realm如果查询不到用户给ModularRealmAuthenticator返回nullModularRealmAuthenticator抛出异常(用户不存在)

         6realm如果查询到用户给ModularRealmAuthenticator返回AuthenticationInfo(认证信息)

         7ModularRealmAuthenticator拿着AuthenticationInfo(认证信息)去进行凭证(密码 )比对。如果一致则认证通过,如果不致抛出异常(凭证错误)。

 

subject:主体

 

Authenticator:认证器( shiro提供)

 

realm(一般需要自定义):相当于数据源,认证器需要realm从数据源查询用户身份信息及权限信息。

 

9.java锁机制的问题

  1. ABA问题

CAS 会导致“ABA问题”。

CAS 算法实现一个重要前提需要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差类会导致数据的变化。

比如说一个线程 one 从内存位置 V 中取出 A,这时候另一个线程 two 也从内存中取出 A,并且 two 进行了一些操作变成了 B,然后 two 又将 V 位置的数据变成 A,这时候线程 one 进行 CAS 操作发现内存中仍然是 A,然后 one 操作成功。尽管线程 one 的 CAS 操作成功,但是不代表这个过程就是没有问题的。

部分乐观锁的实现是通过版本号(version)的方式来解决 ABA 问题,乐观锁每次在执行数据的修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行 +1 操作,否则就执行失败。因为每次操作的版本号都会随之增加,所以不会出现 ABA 问题,因为版本号只会增加不会减少。

  

  1. CAS乐观锁

CAS 是项乐观锁技术,当多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前值。)CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”这其实和乐观锁的冲突检查 + 数据更新的原理是一样的。

  

  1. synchronize实现原理

同步代码块是使用 monitorenter 和 monitorexit 指令实现的,同步方法(在这看不出来需要看 JVM 底层实现)依靠的是方法修饰符上的 ACC_SYNCHRONIZED 实现。

  

  1. synchronizelock的区别

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)。

  

  1. volatile实现原理

在 JVM 底层 volatile 是采用“内存屏障”来实现的

缓存一致性协议(MESI协议)它确保每个缓存中使用的共享变量的副本是一致的。其核心思想如下:当某个 CPU 在写数据时,如果发现操作的变量是共享变量,则会通知其他 CPU 告知该变量的缓存行是无效的,因此其他 CPU 在读取该变量时,发现其无效会重新从主存中加载数据

  

  1. 乐观锁的业务场景及实现方式

乐观锁(Optimistic Lock):

每次获取数据的时候,都不会担心数据被修改,所以每次获取数据的时候都不会进行加锁,但是在更新数据的时候需要判断该数据是否被别人修改过。如果数据被其他线程修改,则不进行数据更新,如果数据没有被其他线程修改,则进行数据更新。由于数据没有进行加锁,期间该数据可以被其他线程进行读写操作。

比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量的查询操作,降低了系统的吞吐量。

  

  1. 说说线程安全的问题

线程安全是多线程领域的问题,线程安全可以简单理解为一个方法或者一个实例可以在多线程环境中使用而不会出现问题。

在 Java 多线程编程当中,提供了多种实现 Java 线程安全的方式:

最简单的方式,使用 Synchronization 关键字

使用 java.util.concurrent.atomic 包中的原子类,例如 AtomicInteger

使用 java.util.concurrent.locks 包中的锁

使用线程安全的集合 ConcurrentHashMap

使用 volatile 关键字,保证变量可见性(直接从内存读,而不是从线程 cache 读)

英斯特笔试:

1.进程和线程的区别

一个程序下至少有一个进程,一个进程下至少有一个线程,一个进程下也可以有多个线程来增加程序的执行速度。

 

2.线程的生命周期,多线程并发会有什么影响

当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。
在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞 (Blocked)和死亡(Dead)
5种状态。

多线程并发的影响:

多线程并发时会不安全,多线程同时操作对象的属性或者状态时,会因为线程之间的信息不同步,A线程读取到的状态已经过时,而A线程并不知道。

 

3.队列和栈的特性。在什么实际案例中见过?

堆栈的特性:先进后出,后进先出

队列的特性:先进先出,后进后出

 
  


4.synchro3ized的作用,java中还有那些常用的锁?

synchronized 它可以把任意一个非 NULL 的对象当作锁。他属于独占式的悲观锁,同时属于可重入锁。

1. 作用于方法时,锁住的是对象的实例(this)

2. 当作用于静态方法时,锁住的是Class实例, 会锁所有调用该方法的线程;

3synchronized 作用于一个对象实例时,锁住的是所有以该对象为锁的代码块。

Java中常见的锁还有乐观锁、悲观锁、自旋锁

  1. synchronized volatile 的区别是什么?

1.volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。

2.volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的

3.volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性

4.volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。

5.volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化

6.springBean的作用域和生命周期

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的名称

  1. 解释什么是事务,多线程事务并发会产生哪些问题,简述事务的四种隔离性

事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。

也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。

事务的结束有两种,当事务中的所以步骤全部成功执行时,事务提交。如果其中一个步骤失败,将发生回滚操作,撤消撤消之前到事务开始时的所以操作。

 

并发问题可归纳为以下几类:

A.丢失更新

B.脏读

C.不可重复读

D. 覆盖更新

E.虚读(幻读)

 

事务的四种隔离性:

A.Serializable(串行化):一个事务在执行过程中完全看不到其他事务对数据库所做的更新

(事务执行的时候不允许别的事务并发执行。事务串行化执行,事务只能一个接着一个地执行,而不能并发执行。)。

B.Repeatable Read(可重复读):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,但是不能看到其他其他事务对已有记录的更新。

C.Read Commited(读已提交数据):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,而且能看到其他事务已经提交的对已有记录的更新。

D.Read Uncommitted(读未提交数据):一个事务在执行过程中可以看到其他事务没有提交的新插入的记录,而且能看到其他事务没有提交的对已有记录的更新。

9.什么是aopspring的几种通知方式

aop是面向切面编程,在整个系统中,动态横向添加新的功能还不改变原来的业务流程的编程

 

spring的几种通知方式:

1.前置通知(方法调用前通知)

2.后置通知(方法调用后正常返回的时候通知,可以获取返回值,发生异常的时候不会执行)

3.最终通知(无论程序是否发生异常都会执行的)

4.异常通知(发生异常的执行)

5.环绕通知(方法执行的时候执行)

10.画出jvm的内存运行图并解释其作用

首先Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.class后缀),

然后由JVM中的类加载器加载各个类的字节码文件,加载完毕之后,交由JVM执行引擎执行。

在整个程序执行过程中,JVM会用一段空间来存储程序执行期间需要用到的数据和相关信息,

这段空间一般被称作为Runtime Data Area(运行时数据区),也就是我们常说的JVM内存。

因此,在Java中我们常常说到的内存管理就是针对这段空间进行管理

  

 

 

  1. 手写快速排序算法

StreamApi

第一种排序:sorted(),自然排序

 @Test

    public void test8() {

        List list = Arrays.asList("aaa","bbb","ccc","ddd");

        list.stream().sorted().forEach(System.out::println);

    }

 

第二种排序:sorted(Comparator com)-定制排序

public void test8() {

        List 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 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 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 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内存泄漏(怎么避免内存泄漏--主要是对象的引用类型)】,垃圾回收机制(新生代老年代的划分,他们分别用什么回收算法,常见的垃圾回收器,他们有什么特点,clientserver模式下运行时他们的垃圾回收器是否相同(扯出来什么叫clientserver模式,什么叫jit))。第二条很重要,两个项目组都问了
③hashmap 1.71.8的底层实现有什么区别,这些改进有什么优点(以及引申出的桶排序(最快的排序,牺牲内存,保证性能),红黑树他的整体结构,四个规则)两个项目组都问了,第二个问的更深。
④spring通过bean创建对象时,怎么确保他的线程安全的
由于我项目上写了websockethttphttps的区别(主要就是后者的加密,他的加密是基于什么的),当两个人之间通讯时,如何确保信息不会被伪造,篡改,截获(主要可逆加密中的对称加密和非对称加密,以及他们之间的配合使用),还有就是信任链当中的证书问题。
抽象类和接口的区别
你使用过哪些线程安全的类,为什么他们是线程安全的,如果给你一个线程不安全的类,你怎么将他改成线程安全的。
构建一个大致的vue
执行一个jar中的某个方法怎么执行。(主要是有一个MANIFEST.MF文件,在这里面配置main class.然后通过java -jar abc.jar)
一台服务器近期cpu和内存突然使用率变高,怎么排查。
说一下单点登录的具体实现
我说我大学学过cjava,他问我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),常用在表单