java架构师面试核心问题

java架构师面试核心问题

源码与视屏讲解同步,但都不是最重要的(具体实现)

最重要的是快速过书,然后回退一遍画圈,重思想

一些特别的实现只要是源码能发布就发布。

视屏讲解里的和源码配合发布,不是源码的不发布。

第一部分:java的高质量编码与性能优化详解

对象包装器与自动装箱

对象包装器类是不可变的,一旦构造了包装器对象,包含在包装器中的内容不会改变。

因为Java方法都是值传递的,所以不能使用这些包装器类创建修改数值参数的方法。如果要编写一个修改参数数值的方法,使用org.omg.CORBA包中的持有者类型,IntHolder、BooleanHolder等。triple(IntHolder x){x.value = 3*x.value }

final关键字

有时候人们希望组织人们利用某个类定义子类,不允许扩展的类被定义为final类。如果将一个类声明为final,其中所有方法自动成为final,但不包括属性。

如果一个属性被定义为final,构建对象是必须初始化这个属性,也就是说每一个构造器执行之后,这个属性的值被设置,并且在后面的操作中不能再对它进行修改。

final修饰符大多数用在了基本类型域或不可变类的域(如果每个方法都不会改变其对象,则这种类就是不可变类)。对于可变类如果使用final修饰会引起混乱。

final关键字只表示存储的变量中的对象引用不会再指向其他的对象的实例,不过对象的值是可以更改的。

方法或者类声明为final主要目的:确保它们在子类不会改变语义。这表明设计者负责进行实现,而不允许子类处理这些问题。换言之如果有一个String类的引用,它引用的一定是一个String对象,而不可能是其他类对象。

static关键字

static修饰的属性,是所有类实例共享的属性。即使没有一个类的实例,static静态属性也存在,它属于一类不属于任何独立的对象。

static final静态常量,每个类对象都可以公有域public进行修改,然而被声明为final则不允许再将其他的对象复制给它了。

静态方法是一种不能向对象实施操作的方法,如Math的pow方法,在运算的时候不使用任何的Math对象。静态方法是没有this参数的方法,静态方法不能访问非静态的属性。

只有两种情况可以使用静态方法

1、一个方法不需要访问对象的状态,其所需要的参数都是通过显示参数提供,如Math的pow方法。

2、一个方法只需要访问类的静态域。

什么时候使用静态工厂方法

1、无法命名构造器,因为构造器要与类名一致,如NumberFormat的货币实例与百分比实例要采取不同的名字。

2、当使用构造器时无法改变所构造对象的类型,而Factory方法将返回一个DecimalFormat这是NumberFormat的子类。

java方法参数的使用情况

java程序设计语言对对象采用的不是引用调用,实际上,对象引用是按值传递的。

一个方法不能修改一个基本数据类型的参数(数值或布尔)

一个方法可以改变一个对象参数的状态

一个方法不能让对象参数引用一个新的对象

关键字this

如果构造器的第一个语句形如this(...),这个构造器将会调用同一个类的另一个构造器。采用这种方式非常有用,这样对公共的构造器代码部分只编写一次即可。

初始化数据的三种方式

1、构造器中设置值

2、在声明中赋值

3、初始化块,一个类中可以包含多个代码块,只要构造类的对象,这些块就会执行。

无论哪个构造器构造对象,属性都会在对象初始化块中初始化,先运行初始化块,然后再运行构造器主体。其实这种机制是不必需的,可以直接放在构造器中。

对类的静态属性初始化,可以使用静态初始化块。在类的第一次加载的这时候将会静态属性的初始化。

继承

定义一个新类以便增加一些新功能, 可以重用类中已经编写的部分代码,并将其中所有的属性保留下来。从理论上来讲,“is-a”关系是继承的一个明显特征,表明了子类的每一个对象也是父类的对象。

使用继承很容易达到节省代码的目的,但是有时候也会被人滥用,假设需要定义一个钟点工,由于按小时计算没有工资,这似乎诱导人们由Employee类派生出子类,在增加一个hourlyWage属性。其实这并不是一个好的主意,这样每一个钟点工都包含了薪水和计时工资两个属性。钟点工不属于特殊的员工。

子类往往比父类功能更加的丰富,封装更多数据。除非所有的继承方法都有意义,否则不要使用继承。

定义子类的时候,只需要将子类与父类不同之处指出来。因此在设计类的时候,将通用方法方法放在父类中,将特殊用途的方法放在子类中。

如果需要调用父类中的方法,而不是子类中的这个方法,可以用super.getSalary()的方式。

同样,子类的构造器不能访问父类的私有属性,所有必须调用父类的构造器对私有属性初始化。调用super()实现对父类构造器的调用。

如果子类没有显示调用父类的构造器,则将自动调用父类默认的构造器,并且父类没有不带参数的构造器,子类构造器中没有显示显示调用其他构造器,则会编译出错。

在覆盖子类方法的时候,不要偏移最初的设计想法。

多态

使用多态而非类型信息

if(x is of type 1)
action1(x)
else if(x is of type 2)
action2(x)

上面的代码无论什么时候都应该考虑使用多态性,action1与action2表示相同的概念,如果是相同的概念就应该为这个概念定义一个方法,并将方法放在两个类的父类或接口中。

动态绑定有一个非常重要的特性,无需对现存代码进行修改,就可以对程序进行扩展。

在覆盖一个方法的时候,子类方法不能低于超类方法的可见性。特别地如果父类方法是public,那么子类放一个要声明为public。

多态在进行类型转换之前,先用instanceof查看一下是否能够成功转换。如果不是某个类的子类将会产生编译错误。

在将超类转换为子类之前,应该使用instanceof进行检查。

一般情况下,应该尽量少用类型转换和instanceof关键字,因为只有在用子类特有方法才需要进行类型转换。如果父类要调用子类的特有方法,那么应该判断一下设计是否有误。

抽象类

为什么要花精力进行这样的高层抽象,每一个人都有类似于姓名这样的属性。现在再增加一个描述的属性,它可以返回一个人的简短描述。在Employee或者Student中实现描述很容易,但是Person类除了name之外一无所知,然后有一个更好的方法使用abstract关键字,这样就完全不需要实现这个方法。

类即使不含抽象方法,也可以将类声明为抽象类。抽象类不能被实例化,也就会说如果将一个类声明为abstract,就不能创建这个类的对象。可以定义一个抽象类的对象变量,但是必须引用非抽象子类的实现。Person p = new Student("")

是否可以省略抽象类中的抽象方法,而仅在子类定义Employee或者Description的方法。这样就不能通过p调用没有声明的方法了。

关于访问权限

最好将类中的属性标记为private、而方法标记为public。任何声明为private的内容对其他类都是不可见的。

有些时候人们希望父类的某些方法或者属性能够被子类访问,这个时候需要将这个定义为protected。

实际开发中尽量少用protected属性,假设其他程序猿可以由这个类派生出其他的新类,并访问其中的受保护域。在这种情况下需要对类的实现进行修改,否则违背OOP提倡的数据封装原则。

protected更有实际意义的使用是限制某个方法的使用,只有子类可以使用这个方法,其他的类不行,如Object的clone方法。

接口

如果类遵从某个特定接口,那么就履行这项服务

任何实现Comparable接口的类都需要包含compareTo方法,并且这个方法的参数必须是Object对象,返回一个整形数组。

为什么不能直接在类中提供一个compareTo方法,而必须实现Comparable接口呢。主要原因是Java程序设计语言是一种强类型语言,在调用方法的时候编译器会检查这个方法是否存在。如果实现了Comparable就可以确保拥有了compareTo方法。

因为Manager扩展了Employee,而Employee实现的是Comparable<Employee>而不是Comparable<Manager>。如果Manager覆盖了compareTo,就必须要有雇员与经理进行比较的思想准备,绝对不能仅仅将雇员转为经理。

如果x是Employee的对象,而y是Manager的对象。调用x.compareTo(y)不会抛出异常,它只是将x和y都作为雇员进行比较。但反过来y.compareTo(x)则会报错。因为子类之间比较的含义不一样,就属于不同类对象的非法比较。应该进行检测if(getClass()!法,并将这个方法定义为final。

接口能声明变量,接口的变量必须引用实现了该接口的类对象

视频去哪了呢?_哔哩哔哩_bilibili

java架构师之分布式专题_哔哩哔哩_bilibili

第二部分:架构思想

架构师的职责

面对一个具体的业务场景的时候能不能想到一个合适的解决方案,并且拆分成一个一个的点,接下来落地需要开发人员执行。

架构如何选型、设计怎么折中、线上遇到问题能不能第一时间解决(Feed系统重要的理念push和pull,push推送到粉丝,粉丝上线再去pull获取数据)

为什么要做架构

互联网的发展,业务员量越来越大,数据越来越多,用户体验越来越高,为了让我们业务快速迭代和产品持续交付,能做到降本提效,业务的需求下驱动架构引进。

架构的发展

单体架构

一个Tomcat中包含了所有的服务,虽然在逻辑上可以通过MVC分开,最终打成了一个war包放在Tomcat下运行,水平分层的。还可以打包放在一个进程rpc服务中。

缺点:系统耦合度高,人员多业务复杂代码的合并上下下线就比较复杂了。技术选型比较单一的。开发效率也较低。

CDA、DNS的解析、静态资源的获取、动态数据库接口的获取

单体架构下,数据量变大、请求量变大、所有业务放在一个单体中性能不够的解决方案

需要对数据库进行拆分:按照业务垂直分库(用户是一个数据库、商品是一个数据库、交易一个数据库),商品还是单表并且数据量有1000亿,单表示没办法满足。这时候需要第二个拆分针对某个表进行水平拆表。

架构拆分,单体的情况下所有的业务写在了一个线程里。垂直拆分按照业务进行拆分,拆为业务一、业务二...另外一方面发现用户还是单体,水平方向按照功能维度继续拆分为网关层、业务逻辑层、数据访问层...

水平分层架构

水平方向会拆分多个进程把一个进程拆分成三个进程,网关是一个进程、业务逻辑是一个进程、数据访问层与数据存储层每一层都逻辑解耦。

分层每一层处理的任务

定长Head包含Uid、SessionId、CMD(请求命令号)、bodyLength

网关层到业务逻辑层数据传输协议是TCP、数据协议使用Protocol Buffers

分层问题:请求路径变长,平均相应的延迟变高,定位问题复杂,运维成本增加。

每层粒度过粗,仅仅是水平分层没有垂直分层。

网关仅仅是验证有没有权限,最终服务间的调用第一是通过ip的白名单,第二是根据每一个请求中的token。

一个功能会被多个业务逻辑层调用

需要把公共的东西下沉为通用的公共业务逻辑层,一定不能有重复代码。

数据库发展的三个阶段

RDBMS(关系系数据库管理系统)-单机管用数据库,数据量不能太大

NOSQL(08-13年,数据量很大,对ACID特性不是很高,分布式非关系数据库,分库分表在DB已经完成,MongoDB、Redis)

NEWSQL(13年-现在,继承NOSQL的扩展性,对事物要求很高,分布式关系数据库TiDB、CockroachDB)

面向服务架构SOA

SOA是进行垂直拆分

通过ESB进行通信,每一个服务是一个单体,SOA不是微服务架构

缺点:每个服务仍然是单体、对ESB严重依赖。

微服务、SOA、与ESB比较

水平架构

同步水平架构

由业务逻辑层驱动的

APP请求第一步通过DNS通过域名获得IP,IP是NetGW(SDN)对路由器进行虚拟化的IP来获取动态接口。

静态接口放在CDN上,通过url获得CDN的url。如果CDN没有命中,CDN会请求Nginx或者对象存储(FastDFS、ceph),把Nginx当做存储静态资源的web容器来做。

异步水平架构

MQ驱动的场景,MVC的架构早已经不再使用,上游和下游的同步架构变为一个异步架构,异步的目的是提高吞吐量,手段是中间需要增加一个第三层。

这个就是第三层MQ,在网关和业务逻辑层中增加一个消息队列目的为了提升吞吐量。上游和MQ继续变为异步,下游是DB,上游通过下游写DB比较慢因为随机写,上游直接写磁盘通过MQ就比较快因为顺序写。

什么情况下可以使用MQ

MQ要保证数据的高可靠与解耦

读请求不能使用MQ,因为读请求要求瞬时拿到请求的结果(如果查询用户只返回OK,是不是用户也叫OK?),写请求可以使用MQ,是否所有的写请求都可以使用MQ,如果对于数据的一致性强不能用异步。

读请求不会更改数据,写请求会改变数据。

只有高并发情况下才会使用MQ,社交场景下吞吐量比较大常用MQ,比如微信里面发消息。

关于读场景与写场景的问题,比如说发一个朋友圈,写入MQ返回成功,这时候马上又发了一个读,因为读不通过MQ,直接读DB,在读的这一瞬间,MQ中的消息有可能还没有到DB,可已经告诉用户成功了,但是用户没有看到发的朋友圈。

这时候需要增加一个本地缓存,因为本地有这个消息,在客户端能看到朋友圈就可以了,等到下次再请求好几秒后已经到DB,解决延迟的问题。

在某些情况下如网络异常等等情况下,没有办法写到DB中的,提示在发一次消息。

MQ的选择

谈谈对Nginx的理解

为什么需要Nginx,做反向代理使用的,在网关前加一个Nginx或者F5会让请求更加健壮性。还有Tomcat中性能不够,在Tomcat中部署两份横向扩展,Nginx上挂上去。

Nginx现在比较稳定了,一般仅仅用来做反向代理或者做一些通用的ip限流。

Redis实现分布式锁

分布式锁的CAP模型,分布式锁是CP模型,Redis是AP模型,用AP模型实现CP模型是完全错误的。

主从的方案,如果主不挂的情况下,Redis是单机和单线程的、可以严格保证锁的时序性。

如果主挂了,来了两个线程申请这把锁,如果一个线程在主上已经拿到锁,另一个线程没有拿到锁。如果主挂的一瞬间,锁没有同步到从Redis上,从Redis没有这把锁。根据哨兵的原理要提升为主继续对外提供服务,另外一个线程继续请求锁的时候又拿到了一把锁,已经拿到了两把锁,高可用这种极端情况下使用Redis方案是不行的。

微服务架构

既按照垂直方向拆分,又按照水平方向拆分

2014年提出的,微服务由许多的小服务进行拆分,比如二手服务里面有用户与商品。用户还要按照网关层,业务逻辑层、数据访问层等等。可以单独运行,独立部署,每一层都可以用不同的语言来实现。

微服务的使用场景:快速迭代与持续交互,要求需求变化比较频繁的,否则使用微服务意义不大。

性能指标,吞吐量会变高,平均相应延时也会变高。而且微服务也不能保证强一致性只能保持最终一致性。

微服务如何拆分

微服务本质是两个维度的拆分,微服务本质不是系统架构,其实有两部分组成,一部分是业务架构,另一部分是组织架构(不是按照职能划分而是按照事业部性质划分)

业务逻辑层之间没有互相调用,只有从上到下的调用,没有从左向右或者从右向左的调用,比如商品需要调用用户的信息通过数据访问层进行。

业务逻辑层垂直拆分为多个服务,存在的问题是公共逻辑层的组件化要引用公共的Jar包,下沉独立服务,提供兼容的接口,会存在重复的代码。这时候还需要堆业务逻辑层水平拆分为基础的业务逻辑层和公共逻辑层。

抽象的公共逻辑层

发布业务逻辑服务、商品列表页逻辑服务、商品详情页逻辑服务、验证码逻辑服务、Push消息逻辑服务...

数据访问层每一层都是rpc服务。

如果服务调用存在循环调用的情况,在这种极端情况下会存在雪崩。

微服务通信选择

REST与RPC

微服务为什么不能使用zookeeper、etcd、consul作为注册中心

真正注册中心ok的是eureka

微服务架构的局限性

微服务的痛点,通信内容还要耦合在进程内。因为业务层要关注服务间的通信会导致业务迭代速度变慢,需要关注的东西越多迭代越慢。

基础设施组件升级困难,影响基础设施团队的交付能力和交付速度

多编程语言之间通信问题,业务每种语言一套基础设施,成本大

通信组件与应用程序耦合在一起的

微服务架构演进

通过Service Mesh来实现,把基础通信功能下推下来成为单独的进程

服务网格的理解

服务网格是一个基础设施层,用于处理服务间通信。云原生应用(Docker,k8s)有着复杂的服务拓扑,服务我能够给负责在这些拓扑中实现请求的可靠传递。在实践中,服务网格通常实现为一组轻量级网络代理,它们与应用程序部署在一起(pod),而对应用程序透明。

业务程序再也不用处理通信,服务网格进行通信。

未来服务的趋势,无论是无状态服务还是有状态服务,都跑在容器上云原生应用中。

Docker与k8s

Pod容器上,进程跑在Docker上

Service Mesh架构

由4个进程进行处理,因为应用程序与Sidecar通过TCP与pb进行通信各个语言都支持的,这时候不管应用程序用什么语言实现都可以满足。

因为服务降级与熔断已经下沉一个独立服务,这时候要升级不依赖业务团队即可升级,迭代速度增快。

linked就是Sidercar代理,通过注册中心找到远端linked的地址进行通信

Service Mesh优点

独立进程、独立升级,业务团队专注与业务逻辑本身,一套基础设施支持多种语言开发,业务团队和基础设施团队物理解耦。

Iaas、Paas、Saas服务模式的区别

CAP模型

C一致性,A可用性

配置中心的比较

Apollo(阿波罗)是携程框架部门研发的开源配置管理中心

网络通信一个请求的过程

一个request到网关,请求做的第一件事情是收包和发包,通过I/O线程把收集到的包放在队列中。接下来有一个工作线程从队列中取出来处理请求和短连接与长连接没有关系。与TCP与RCP无关。

做IM一定要用TCP长连接与长轮询

高可用的设计手段

总会遇到一些突发流量的情况,不能因为突发流量导致服务宕机的发生。

高可用是指保证7*24,任何人可以访问任何的服务在任何时间和任何地点可以得到任何结果都一致。

网络划分,CAP ,CA没有网络划分不存在的,当机房有多个硬件网络可以划分,是否可用

评价高可用的方式,一段时间比如一年的停机影响请求量的占比,停机时间影响请求量/总的请求量

无状态化表明服务完全对等的,通过布置冗余的服务可以通过不同网关访问以及两台机器上的Session用户信息是完全对等的-保证在不同的机房、机柜与机架上防止停电造成的不稳定发生。

负载均衡,如果某一层服务挂了,怎么发现已经挂了并且踢掉,怎么把请求转移到另一台服务上以及网关怎么快速的回复。

服务幂等设计,服务请求一次与请求多次结果是一致的,业务幂等,库存只有一个多个用户去抢商品,保证商品不多卖,不重复下单等等,幂等往往依赖分布式锁的实现。

异步化更多是高并发的设计,目的就是为了提升吞吐量。请求不是关键流程,不太关心返回的结果可以使用异步化。

核心的流量使用同步的方法来做,架构中主流流程变少了。

缓存提升响应时间,减少对数据库的压力,数据库不容易出现瓶颈。

服务分级降低故障的发生,也是高可用的一种手段

幂等设计

解决重复消息的问题。请求幂等(请求重试),读请求不需要做幂等,因为读请求不会对数据发生改变,写请求需要做幂等。

所有请求层面只有数据访问层直接操作DB与Cache会对数据发生改变。

插入用户的时候,最好使用业务主键,让主键有业务的信息。自增主键用户有token的概念,用户的唯一标识,唯一的索引。

天然幂等是绝对值的修改,修改的时候可以增加Where条件或者把相对修改变为绝对修改。

如果难以控制尽量使用串行化替代并行化。要么都成功要不都失败(分布式事务问题)

业务幂等(通过分布式锁解决本事是隔离问题),同一用户不重复下单,商品不超卖,MQ消费端去重。

多个订单消息的时候oid变为共享资源,在下游消费的时候,冗余部署多份服务,共享一个oid,需要对共享资源进行串行化(锁)

分布式事务

状态修改,订款打款不再一个DB中,本地事务不行了。

数据一致性:对任何人在任何时间的任何地点以任何的接入方式对于任何服务产生的数据都是一致的。

数据不一致的原因:实施微服务架构以后有多个DB,还有多个缓存

本地事务,MQ发送消息超时不知道是成功还是失败,不知道如何处理。

发送消息本质不是原子的动作

解决分布式事务

事务的三个要素(AP应用程序的发起方、RM要操作谁数据库、TM分布式事务本身)

TCC对业务侵入大一般使用较少

刚性事务无业务改造,TM去做,业务层直接调用即可。

MQ发送方就是业务放,首先MQ发送方发送办消息到MQ Server,返回消息知道本地事务到底有没有执行成功。根据返回的commit或者rollback的时候,如果消息丢失,MQ Server端有一个线程定时扫描办消息,MQ Server未收到回查事务状态,回查本地事务表去确定本地事务到底成功还是失败,会对业务方有侵入。

把消息和订单表放到本地数据库里,消息写入MQ成功,MQ Server给微服务一种回值,MQ 客户端再到数据库把订单表对应的数据删除。

发送端不幂等,分布式锁进行去重操作。

A产生自己的DB,通过机制把消息写入MQ中,B读出来进行消费,B有自己的DB2、C有自己的DB3,B与C去重部分解决,B与C只做自己的本地事务即可。

如果B成功C失败,一定要回滚去补偿,我们的补充不是通过架构机制做的(很复杂),通过人工介入补偿。

基于回查的半消息其实用的不多(RocketMQ),用的最多的是本地事务消息表。

同步场景更复杂

如果C失败了,直接告诉用户失败,然后异步的补偿A与B,不是阻塞主流程去做。

进行补偿的时候,需要记录每一次请求的上下文(请求调用的参数)

TDB根据分布式专门建立的事物,B先记录A的调用参数,写在TDB中,然后做A的本地事务。

C会先把请求调用参数写在TDB中,接下来调用C的事物,一旦事物成功了什么都不需要做结束,记录的内容自动报废。

如果C失败了,这时候要把状态从1改为3,马上返回网关失败了

保存ThreadLocal内方便在线程内去共享TXID标识事务组。在进程间进行共享,在方法开始前

分布式锁

解决就是去重的问题,对共享资源进行串行化。分布式环境下锁定全局唯一资源,实际表现互斥锁。

MQ发送端去重会发送多次(不能去重),消费端也要去重。消费端根据messager里面的订单ID,共享资源的去重问题,因为下游有可能冗余部署到多个进程在不同机器上,需要分布式锁解决问题。

如果发送端一个订单Id,生产多个messager,消息最终都会到MQ中,消费端从MQ中把消息取出来根据oid(通过分布式锁把oid作为共享资源,分布式锁申请一个key和一个ttl,两个进程谁拿到oid这把锁谁去执行这件事)进行去重。

Redis本身是单线程的,天然串行化处理。

锁和Session的生命周期是有限的,没必要进行持久化的操作,如果一个线程访问Redis,Redis这时候挂了,另一个线程去访问Redis还会拿到一把锁。单点问题,T1与T2在的极端情况都会拿到锁。在交易的业务不能接受,在非交易场景使用Redis可以接收。

选择CP(可用性要求高)还是选择AP(Redis主从的复制是半同步),分布式锁。

存储模型的选择

当集群规模大了以后,日志需要些,etcd成为瓶颈,定期清理事件的通知。

v3版本自动续约,遇上客户端的自动假死,服务端重启客户端与etcd的长连接不存在,自动踢掉。

业务逻辑层把业务消息存在HBase,每次去HBase会很慢,有些消息要做去重(订单库存),把消息直接放在etcd中分布式锁解决。不希望有重复消息,在入库前要判重判断掉(分布式锁进行去重)。

如何无缝停止线上服务

更新系统不关闭服务,请求到网关,网关具备了热切换的功能(所有的请求都拒绝)

比如8点关机,就要保证8点前的请求都要处理完,怎么保证8点这一时刻的请求处理完,可以根据超时时间决定什么时候启动热切换功能。

如果网关不具备热切换功能,这时候限制IPTABLE让请求只出不进。

高并发的设计手段

高并发关注吞吐量与响应延迟

横坐标用户数,纵轴绿线为应用率,红线吞吐量,蓝线响应时间。

随着请求量增加在最优的请求点上出现拐点,基本利用率与吞吐量增长会很慢了,如果继续增加的话耗时会急剧增加。

总之,调用了多少RPC接口,载入了多少数据,使用了什么算法,非核心流程能否异步化,没有数据依赖的逻辑能否并行执行。

系统做一些微服务化,每一层都可以动态的进行扩展,性能自然提升了。

大并发案例

层层通过缓存过滤,把不符合要求的及早的过滤掉。

秒杀系统库存数据在缓存中,逻辑层会与cache交互获取库存,如果还剩余的话,可以访问数据库减库存。

然后把现在剩余的库存还要更新到cache中,或者放到MQ中。

朋友圈消息发送系统

无状态设计

无状态就是保住冗余设置的进程完全一致,设计的目的就是快速扩容与弹性缩容相结合。

用户Session存在离用户最近的地方,一定会存在网关层。

两种存放方式,一直接存放在网关层,二存放在外部存储

存储在网关层(单点、网关层有状态化不可以行的)

Session还是存在网关中,每一个网关存全量数据,容易产生网络风暴与脏读

Session数据放在客户端,存在分布式的设备上(费流量与电流)

因为Session要存储的,所以无论如何都没法做到无状态化,网关做成无状态化的,Session作为高可用的集群(Redis)

Redis主存本身还是单机,每台机器保存的Session数量还是有限的,才有了主从分片。

对客户端每一个网关层来说都要知道每一个用户落在那一台上,路由规则放在网关层上。

如果不用再网关根据用户做一些取模,来决定落在哪一个Redis主存上,再加一层poxas屏蔽掉网关层来知道分片规则这样一件事情(Codis与Redis的实现)。

如果想让128片主从的集群变为256片集群,数据需要做迁移,一定要在低峰期做这件事。

狭义的负载均衡

如果在用户的角度知道代理的存在就是正向代理,反之就是反向代理

广义的负载均衡

如果服务挂了,通过zk注册中心发现。注册中心注册ip、port、Name。

App到网关层采用Http通信,一旦到了Service层,采用TCP长连接通信

Http通信的不足:头加body数据传输的效率不高,往往的短连接,频繁的释放连接

如果发现服务挂了,直接从连接池中提出挂掉的服务。

通过zk不能发现挂掉服务的所有问题,当机器挂了心跳没了,但是业务逻辑层假死了(线程已经不存在了),但是心跳一直在(续租的锁还在),网关层发往业务逻辑层总会有返回值,根据返回值的错误比例去判断服务有无问题。

如果线程假死了,上游就已经知道这件事了,上游会把网关熔断掉,会发送请求到控制中心(RPC服务)。控制中心回到业务逻辑层的agent,agent先jstack两次再kill线程,然后sleep最后本地重启线程服务,这时候心跳也就自动关机杀死了。

Flume和Kafka完成实时数据采集

spark实时计算程序计算耗时

杀死进程怎么保证进程内存队列里请求都处理完毕

发一些kill-i的信号,服务收到信号后首先会拒绝接受请求,然后把内存的请求全部处理完最后才自杀,不能直接杀死进程。

服务降级设计

处理请求的第一件是收包,IO线程把请求放在内存队列中,工作线程去处理队列中任务。

工作线程一定,请求太多了处理不完,必须丢掉一部分请求。

优先级,所有请求都有定长的head,uid,cmd,sessionId,bodyLength,cmd每一个请求的命令号,丢弃策略丢非核心请求,核心请求放在队列中,因为命令要随时调整的,所以队列放在配置中心。

服务熔断

熔断当下游超时响应慢,脱跨整个服务,如果没那么在乎返回null,否则根据fallback方法去返回业务的返回值。

熔断另一些目的,一些非关键路径熔断对业务影响不大,也可以熔断。

服务管理平台就是治理中心

熔断放在RPC客户端

滑动窗口计算10个单位(10s)计算耗时的情况

实现根据情况放在队列中定期去实现

快速迭代保证质量,服务出现及早发现,最早影响1%

因为灰度的策略可以动态改变的放在Apollo配置中心,那些服务可以进行灰度,灰度的策略。

灰度通过请求上游服务,把请求转到下游服务

配置中心要下发命令到上游,Apollo没法继承Ngnix,

新版服务发布,发布一个新的版本号到注册中心,刚发不上去不能生效,有一个版本号的控制,打开才能访问新的版本号。

版本号本身在注册中心,版本号的开启放在配置管理平台实现。

多模块的同时灰度,网关层和数据访问层同时灰度,这时候流量必须打tag才能继续做灰度。

设计两个模块,这时候必须对流量打标签,对tag字段赋值,业务逻辑层对带T的流量请求都会转到新版的数据访问层。

字段不一致,数据迁移一定要老的数据同步一份到新的库中。

对搜索业务逻辑实现ABTest,在网关还是业务逻辑去做。

直接在搜索层的业务逻辑实现,谁依赖这个谁去做,网关层是无逻辑的,一旦开启到任何一层都可以使用。

压测做服务隔离流量隔离,那些压测流量那些是真实流量

互联网注册中心

etcd、eureka、consul

dubbo成就了zookeeper,分布式的协调器做分布式同步的功能和群组的功能

zk最核心的攻是内部的文件系统,所有的节点在zk内部就是一颗树,一个一个的叶子节点

通知机制是长连接实现的

节点选主,服务冗余部署3分,基于zk的选主往特定的目录下写文件,谁最终写文件成功谁为主

配置管理,除了存ip和port还可以存储超时等时间

除了etcd做分布式锁,etcd生态和社区活跃度与性能更好,都是cp模型

高可用的主从切换,写文件谁成功就是主,主挂了连接没了心跳,每个节点继续写文件确定主

因为eureka追求最终一致性,不需要什么数据协议。

多数据中心就是多机房,多个机房只部署一个zk,网络划分zk节点不可用。

watch支持,zk的消息端要主动push消息给zk客户端,长连接很适合做。Long Polling 客户端向server端发请求,HTTP的一旦返回response,连接就断了,继续发。

服务发现不是cp模型,是AP模型

只有几百台机器的规模几千台仍然使用zk会把zk集群打爆。

注册中心就是function函数,根据服务名去查询服务可用列表。

service的流量不均衡,有些只有一份流量有些却又两份流量。

假设机房三出现网络分区(可以读不可以写),机房内的网络还是通的。

因为zk是cp模型,一旦网络划分就从整个集群不可用了,有一个节点连不上整个服务节点就不可用了。

因为是cp模型,写在zk里面任何一个写入节点保证cp,要在从节点重新写一份,包括心跳信息,节点注册,全部会在集群注册,大量写的请求。因为paxos协议,zk内部选一个zk主,其他是zk从,真正写节点写在zk主(单点),不能水平扩展。

分集群,房产是一个zk集群。每一个业务有一个zk集群,破坏了整体服务的连通性。服务调用应该在一个zk中。

所有都是事物提交,2pc

假死了不知道keepaline机制不好,发一个ping命令

定期发送服务看是否假死,如果假死及时踢掉

业务逻辑连接到注册中心,想熔断服务

服务分组提供物理隔离,冗余部署了多份,很多情况下调用方不是对等的(有些服务是核心的服务),互相之间没有影响

指令下发功能

注册中心

服务管理平台提供服务管理的功能,服务的展示以及入口

服务控制中心,注册信息的储存

控制中心,注册信息的存储,另外做指令的下发,信息上来存在mysql中。

服务注册通过业务逻辑层与ccs,服务发现直接网关连接服务发现平台与控制中心没有关系。

注册新消息放在mysql中,服务调用与服务管理平台通过http协议去实现

拉到网关层的信息,网关会直接和服务管理平台交互,只要传递loginName,通过ccc与控制中心交互,拉取到业务逻辑层对应点ip信息等。服务发现一定都通过控制中心走的。

服务管理平台除了要拉取注册和熔断信息以外,每一个模块流量的监控和质量的监控要上报服务管理平台。

所有的下发都推给控制中心。由控制中心统一发送到服务调用方。

网关上层是Nginx,网关是无状态的,网关可以冗余部署很多份,Nginx(lvs、f5)反向代理就可以了。

注册中心服务注册流程

服务容器spring boot

提交指令到服务管理平台,服务管理平台组装报文,

在一个进程中

节点信息:ip信息port,Name,服务器类型,权重,分组信息,服务版本号

主动下线一台机器,通过服务管理平台去提交发送指令到控制中心,控制中心再发送给ccp,主动推送。

业务逻辑层有一个节点响应比较慢,可以在服务管理平台,让网关屏蔽掉业务逻辑层其中一台响应比较慢的。

集群无状态的,

Gossip协议做服务的发现与状态的转义和心跳

配置信息放在数据库与本地不是很灵活,需要统一存储和可视化的管理。

配置和程序是解耦的,不同环境配置可以不一样

注册中心是AP模型,配置中心也是AP模型

Meta存域名和ip的对应关系,请求DNS直接随机选择meta Data,eureka返回ip列表,客户端获得全然ip信息,直接通过访问服务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Java架构面试宝典是一本针对Java架构面试准备的参考书籍,主要内容包括Java基础知识、Java框架、数据库、操作系统、网络、性能优化、架构设计等方面的知识点和题目。在面试准备中,这本书可以帮助Java架构提高对知识点的掌握程度和把握面试节奏。 Java架构面试题通常涉及的知识点很广泛,因此需要对多个方面进行了解和掌握。对于Java基础知识,包括面向对象、异常机制等这些基础内容,建议要牢记相关概念和使用方法。对于Java框架,包括Spring、Hibernate、MyBatis等常用框架,掌握其核心原理和使用方法是必须的。对于数据库,包括MySQL、Oracle、MongoDB等,需要了解其基本使用方法和优化细节,以及对SQL语言的掌握程度。对于操作系统和网络,需要了解Linux、Windows等常用操作系统的基本操作和命令,以及TCP/IP协议等网络协议的基本原理。对于性能优化和架构设计,需要对代码、数据库、网络等方面有一定的优化思路和方案。 总的来说,Java架构面试宝典提供了大量的面试题和知识点,并为读者提供了一些解答思路和解决方法。但是,仅有这本书是不够的,还需要对知识点进行巩固和练习,逐渐提高自己的实践能力和应变能力,才能在面试中展现出优秀的表现。 ### 回答2: Java架构面试宝典是一本好书,它的作者通过大量的面试题目和答案,帮助Java架构更好地应对面试。书中的面试题目分为Java基础、多线程、JVM、分布式、算法、操作系统、数据库等等方面。对于想要成为Java架构的人而言,这本书是一本很好的入门指南。 书中的答案很实用,能够帮助读者更好地理解Java架构面试题目。作者在答案中提供了很多实际的案例,让读者更容易理解和掌握相关的知识点。同时,答案也针对每个面试题目提出了符合面试官口味的答题思路和技巧,帮助读者在面试时更好地回答问题。 总的来说,Java架构面试宝典是一本值得推荐的书籍。它可以帮助读者更全面地掌握Java编程知识,并且在面试时更加自信。但是需要注意的是,仅仅掌握了面试题目并不能代表掌握了Java编程,读者在看完书后还需要继续学习和实践。 ### 回答3: Java架构是一个高级职位,需要具备很多技能和经验。Java架构面试宝典和答案是帮助候选人为面试做好准备的资源。这本面试宝典覆盖了Java架构面试的各种问题Java架构需要掌握的技能包括Java编程,数据库管理,互联网技术等等。这些技能在Java架构面试中都会被提及。候选人需要准备好针对这些技能的答案,如何解决相关的技术问题。 除了技术相关的问题Java架构面试还需要考察候选人的项目管理能力,领导能力和团队协作能力。候选人需要准备好面对类似的问题,例如: 1. 推动项目进展的困难点是什么? 2. 你在团队中的领导角色是什么? 3. 如何处理团队成员之间的冲突? 候选人还需要准备好自我介绍,包括自己的职业发展历程和技能。此外,候选人还需要知道如何回答一些常见的面试问题,例如: 1. 你最擅长的编程语言? 2. 你最喜欢的Java框架是什么? 3. 你能否谈谈你在Java开发中遇到过的最大问题? 综上所述,Java架构面试宝典和答案为候选人提供了很好的参考和准备。候选人可以预见面试问题,精心准备自己的答案,以获得更好的面试表现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wespten

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值