.NET面试笔记
开场白
您好你是***来面试我们公司.net岗位的吧!我是***面试官
首先非常感谢您来参加我们公司的面试,下面我们就正式开始这场愉快的面试吧!
麻烦您先简单的做一下自我介绍
中场技术提问
嗯,刚刚我也听了您的一些介绍 我这边主要提一下一些技术点
基础
1. class和struct,record的区别?
Class可以被实例化,属于引用类型
Struct属于值类型
Record 记录是一个语法糖,本质上还是class或者struct,它只编译时生效,运行时并没有记录这个东西,此外,根据官网介绍,记录不适合在EntityFrameworkCore中使用,毕竟它重写了Equals方法和相等运算(==和!=),这样可能会对EntityFrameworkCore的实体跟踪机制造成影响
2.C#中类成员的访问修饰符有哪些?
private : 私有成员, 在类的内部才可以访问。
protected : 保护成员,该类内部和继承类中可以访问。
public : 公共成员,完全公开,没有访问限制。
internal: 在同一程序集内可以访问。
3.面向对象的三大特征是什么?/面向过程和面向对象的区别
封装,继承,多态;
面向过程和面向对象都是解决问题的逻辑方法,分析问题既可以用面向对象的方法 来思考,也可以用面向过程方法来解决,但两者还是有一定区别
面向过程:是分析问题并解决问题所需要的步骤,强调是解决问题的步骤。可以先定义多个函数,在使用的时候逐步调用函数即可
面向对象:是把问题分解成多个对象,强调的是解决问题的行为表现
4. 抽象类和接口的区别?
接口支持多继承;抽象类不能实现多继承。
接口只能定义抽象规则;抽象类既可以定义规则,还可能提供已实现的成员。
接口是一组行为规范;抽象类是一个不完全的类
接口可以用于支持回调;抽象类不能实现回调,因为继承不支持
接口只包含方法、属性、索引器、事件的签名,但不能定义字段和包含实现的方法;抽象类可以定义字段、属性、包含有实现的方法
接口可以作用于值类型和引用类型;抽象类只能作用于引用类型。
5. 接口可以包含哪些成员?
接口可以包含属性、方法、索引指示器和事件,但不能包含常量、域、操作符、构造函数和析构
6. 什么是装箱和拆箱?
从值类型转换到引用类型装箱。从引用类型转换到值类型拆箱
7. C#中,哪些类型是值类型?哪些类型是引用类型?(数据类型)
C#的引用类型包括:数组、委托、接口、object、字符串、用户定义的类。
C#的值类型包括:数值类型、结构体、bool型、枚举、可空类型、用户定义的结构体。
8. 值类型和引用类型的区别?
值类型:占用空间固定、保存与复制的是值本身、使用typeof检测数据的类型、基本类型数据是值类型
引用类型:占用空间不固定、保存与复制的是指向对象的一个指针、使用instanceof检测数据类型、使用new()方法构造出的对象是引用型
9. C#中的基本数据类型有哪些?
值类型:有符号整数(由小到大):sbyte、short、int、long 无符号整数(由小到大):byte、ushort、uint、ulong 浮点型:float、double、decimal
布尔型:bool
字符型:char
引用类型:string、object
10. 重写和重载的区别?方法的override和overload有什么区别?
重载:同类同名不同参,属于编译时多态。重载是在一个类中定义多个方法名相同的方法,表示一个行为有多种实现方式
重写:同名同参不同类,属于运行时多态。重写是用子类的方法来重写父类的方法,表示相同的方法在父类和子类中具有差异性
11. 构造函数是否可以被继承?是否可以被重写?
构造函数不可以继承,因此不能被重写,但可以被重载
12. C#中,运算符“?”和“??”的区别是什么?
一个个问号(?)的运算符是指:可以为 null 的类型
如:
int? x = null;//定义可空类型变量
i=3?2:5 //还可为三元运算符
两个问号(??)的运算符是指null 合并运算符,合并运算符为类型转换定义了一个预设值,以防可空类型的值为Null
如:
nt? y = x ?? 1000;//使用合并运算符,当变量x为null时,预设赋值1000
13. C#中,用const和readonly,init修饰的字段有什么区别?
readonly (只读)和const(常量)都是用来标示常量的。
初始化赋值不同。
const修饰的常量必须在声明的同时赋值
init 总的功能就是扩大readonly实例字段的赋值方式,C#9之前readonly字段只能在字段初始值设定项和实例构造函数中赋值。C#9 推出init 以后,readonly实例字段可以多一种赋值方式,可以在对象初始过程中通过属性和索引器赋值,也就是说可以通过对象的初始值设定项 赋值。
14. C#中,new有几种用法?
1)new 运算符:用于创建对象和调用构造函数。
2)new 修饰符:在用作修饰符时,new 关键字可以显式隐藏从基类继承的成员。
3)new 约束:用于在泛型声明中约束可能用作类型参数的参数的类型。
15. C#中,ref和out在修饰方法参数时有什么区别?
ref和out都可以实现将一个指定的参数按照引用进行传递:
ref参数在传入方法之前必须进行初始化;而out参数在传入方法之前可以不用初始化。
ref参数在方法内部可以直接使用;而out参数在方法内部不能直接使用。
ref参数在方法内部可以使用也可以不使用;而out参数在方法返回之前必须对其赋值。
16. C#中using有几种用法?
using指令,即引用命名空间。
using 定义对象的使用范围,即时释放对象。
using别名(using 别名=包括详细命名空间信息的具体的类型)
17. C#中&和&&,|和||有什么区别?
&:不管前面的条件是否成立,后面的条件依旧会判断
&&:前面的条件不成立,后面的条件就不会判断
| 和 || 同上
18. C#中is和as有什么区别?
is是用来判断类型A能否转化为类型B,一般用来作有继承关系的父类与子类是否可以相互转化的判断。
as是将类型A转化为类型B,一般也是用来做子类与父类的转换
19. 什么是委托?事件跟委托是什么关系?
委托是一种方法容器,里面可以装载若干个方法引用地址,调用委托,就相当于同时调用了该容器内的所有方法。 委托可以将方法作为参数传递给方法.委托主要用来动态调用方法的。事件基于委托,事件的本质是委托字段的包装器,对委托字段的访问起限制作用,
事件隐藏了委托实例的大部分功能,仅暴露添加和移除事件处理器的功能。
20. async/await的用法
异步方法:在执行完成前立即返回调用方法,在调用方法继续执行的过程中完成任务。
async/await 结构可分成三部分:
(1)调用方法:该方法调用异步方法,然后在异步方法执行其任务的时候继续执行;
(2)异步方法:该方法异步执行工作,然后立刻返回到调用方法;
(3)await 表达式:用于异步方法内部,指出需要异步执行的任务。一个异步方法可以包含多个 await 表达式(不存在 await 表达式的话 IDE 会发出警告)。
.NET
1. IOC容器注入的方法有那些?
构造函数注入
属性注入
方法注入
2. .net默认IOC容器?
Microsoft.Extensions.DependencyInjection
3. 什么是ASP.NET中间件?
中间件是ASP.NET Core的核心组件,MVC框架、响应缓存、身份验证、CORS、Swagger等都是内置中间件
广义上来讲:Tomcat、WebLogic、Redis、IIS;狭义上来讲,ASP.NET Core中的中间件指ASP.NET Core中的一个组件。
中间件由前逻辑、next、后逻辑3部分组成,前逻辑为第一段要执行的逻辑代码、next为指向下一个中间件的调用、后逻辑为从下一个中间件执行返回所执行的逻辑代码。每个HTTP请求都要经历一系列中间件的处理,每个中间件对于请求进行特定的处理后,再转到下一个中间件,最终的业务逻辑代码执行完成后,响应的内容也会按照处理的相反顺序进行处理,然后形成HTTP响应报文返回给客户端。
中间件组成一个管道,整个ASP.NET Core的执行过程就是HTTP请求和响应按照中间件组装的顺序在中间件之间流转的过程。开发人员可以对组成管道的中间件按照需要进行自由组合
4. 中间件的三种注册方式?
Use方法最明显的特征是
传入的委托中含有一个委托参数,我们一般用next来接收这个委托参数,通过调用next.Invode()方法来调用下一个中间件,这样Use之后的中间件才能够被执行
Run方法区别于Use方法的最明显差异是
Run相当于一个终结点,Run之后的中间件不会被执行,因为它不像Use一样可以调用next.Invoke();
Map中间件最明显的特征是只有访问特定的路径才会执行
5. 依赖注入的生命周期?
Transient:即用即建,用后即弃。就是每次获取这个服务的实例时都要创建一个这个服务的实例。
Scoped:这种类型的服务实例保存在当前依赖注入容器(IServiceProvider)上。在同作用域,服务每个请求只创建一次。
Singleton:单例。即只保存一个服务实例
数据库
1. ADO.NET操作数据库的关键字?
Connection :连接对象 ----用于对数据库的连接操作,参数是连接字符串
Command:命令对象----用于执行对数据库的操作,参数是连接字符串或存储过程,也必须传入连接对象实例
SqlDataReader:数据读取对象----用于读取操作,Read()方法返回true或false判断是否读到了数据。
DataSet:数据集对象----类似于内存中多张虚拟的表,可以动态的添加行列数据,对数据库进行更新回传操作
SqlDataAdapter:数据适配器对象----该对象可用于数据库的增删改查操作,一次性将读取到的内容加载到内存中,可以脱离连接进行操作,返回到一个DataSet对象.
2. 事物有哪些特性和含义?
原子性就是将事物进行的操作捆绑成一个不可分割的单元,事物中进行的数据操作要么全部成功,要么全部失败(回滚)。
一致性是指事物使数据库从一个一致性状态转换到另一个一致性状态。也就是数据库前后必须处于一致性状态。
拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
多个用户并发访问数据库,数据库为每个用户开启一个事物,每个事物相互独立,互不干扰。
事务的隔离性主要规定了各个事务之间相互影响的程度。隔离性概念主要面向对数据资源的并发访问(Concurrency),并兼顾影响事务的一致性。当两个事务或者更多事务同时访问同一数据资源的时候,不同的隔离级别决定了各个事务对该数据资源访问的不同行为。
事务的隔离级别,隔离程度按照从弱到强分别为“Read Uncommitted (未提交读)”,“Read Committed(提交读)”,“Repeatable Read(可重复读)”和“Serializable(序列化)”。
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
3. ORM框架和EF的区别?
ORM是一种思想,而EF是实现这种思想的一种框架。将表实体的变化,通过ORM框架映射到数据库表的变化。即在代码中所添加的实体,可以通过EF框架映射到数据库表中。
4. Sqlserver varchar 和nvarchar的区别?
varchar是非Unicode可变长度类型,nvarchar是Unicode编码可变长度类型
两者最大长度不同 varchar 1-8000 nvarchar 1-4000
varchar存储的字节就是设置的长度,nvarchar存储的字节是本身的长度乘2 假定varchar长度为4那么只可以存储2个汉字,但是nvarchar可以存储4个汉字
5. drop truncate delete区别?
drop(丢弃数据): drop table 表名 ,直接将表都删除掉,在删除表的时候使用。
truncate (清空数据) : truncate table 表名 ,只删除表中的数据,再插入数据的时候自增长id又从1开始,在清空表中数据的时候使用。
delete(删除数据) : delete from 表名 where 列名=值,删除某一列的数据,如果不加 where 子句和truncate table 表名作用类似。
6. count(*) count(1) count(id) count(列区别)
count(expr)函数解释
count(expr) :返回select语句查询的行中expr的值不为null的数量。结果是一个bigint值。
count(1) 和 count(*) 表示直接查询符合条件的总表行数。
count(列名)表示的是查询符合条件的列且值不为null的总表行数
count(*)原理
MyISAM引擎中直接在磁盘存了表的总行数,因此执行count(*)的时候直接返回总行数
InnoDB引擎中没有直接存表的总行数,因为执行count(*)的时候需要把数据一行一行地从引擎里读出来,然后累积计数
count(*)是SQL92定义的统计行数的标准语法,所以MySQL数据库进行过很多优化。InnoDB优化器专门且只给count(*)*做了取行数的优化(优化的前提是查询语句中不包含where条件和group by条件)其他的几种count()都需要作判断。我了解的优化如下:
例如普通索引和聚集索引都能统计出所有行数,则会选择小的索引树来扫描,聚集索引的叶子节点是数据行,而普通索引的叶子节点只是id,所以会选择普通索引扫描。如果没有,最差也是使用聚集索引。所以优先使用它。
count(id)原理
count(主键id)会表示直接扫描id列,然后取出id作判断是否为空,不为空则累加。它只是count(列)的一个特例。
count(1)原理
官方文档说过,count(1)和count(*),MySQL的优化是完全一样的,根本不存在谁更快。但是但是count(1)它在扫描到一行时不会取任何值,而是直接返回一个1,然后累加1的个数。
但你仍然要用count(*),因为这是SQL92定义的统计行数的标准语法。
count(1)只是count(常量)中的一种,所以count(1)你也可以写成count('x') ,此时就是统计x的总个数就为行总数。
count(列)原理
count(列)会把对应字段取出来,然后判断是否为null,不为null则累加1。很明显多了一个取值再判断的过程。
选择
count(1) = count(*) > count(主键id) > count(列)
7. Mysql大表加索引?
Mysql 5.6以后
ALTER TABLE tbl_name ADD PRIMARY (column), ALGORITHM=INPLACE, LOCK=NONE;
Mysql5.6之前
可在通过 “影子拷贝”来解决-弊端没有考虑新增加和update 数据 就是 先创建一张和源表无关的新表,然后通过重命名和删表操作交换两张表;
8. 主键和唯一索引的区别?
主键是一种约束,唯一索引是一种索引;
主键创建后一定包含一个唯一性索引,唯一性索引不一定是主键;
唯一性索引列允许空值, 主键不允许;
主键可被其他表引为外键,唯一索引不能;
一个表只能创建一个主键,但可创建多个唯一索引。
9. 聚集索引和非聚集索引的区别?
1.聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个
2.聚集索引存储记录是物理上连续存在,而非聚集索引是逻辑上的连续,物理存储并不连续
3.聚集索引:物理存储按照索引排序;聚集索引是一种索引组织形式,索引的键值逻辑顺序决定了表数据行的物理存储顺序。
4.非聚集索引:物理存储不按照索引排序;非聚集索引则就是普通索引了,仅仅只是对数据列创建相应的索引,不影响整个表的物理存储顺序。
5.索引是通过二叉树的数据结构来描述的,我们可以这么理解聚簇索引:索引的叶节点就是数据节点。而非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块。
优势与缺点:
聚集索引插入数据时速度要慢(时间花费在“物理存储的排序”上,也就是首先要找到位置然后插入),查询数据比非聚集数据的速度快。
10. 脏读(Dirty read)?
当一个事务正在访问数据并且对其进行了修改,但是还没提交事务,这时另外一个事务也访问了这个数据,然后使用了这个数据,因为这个数据的修改还没提交到数据库,所以另外一个事务读取的数据就是“脏数据”,这种行为就是“脏读”,依据“脏数据”所做的操作可能是会出现问题的。
11. 修改丢失(Lost of modify)
是指一个事务读取一个数据时,另外一个数据也访问了该数据,那么在第一个事务修改了这个数据之后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,这种情况就被称为修改丢失。例如:事务1读取表中数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果都是19,但是事务1的修改记录丢失了。
12. 不可重复读(Unrepeatableread)
指在一个事务内多次读取同一数据,在这个事务还没结束时,另外一个事务也访问了这个数据并对这个数据进行了修改,那么就可能造成第一个事务两次读取的数据不一致,这种情况就被称为不可重复读
13. 幻读(Phantom read)
幻读与不可重复读类似,幻读是指一个事务读取了几行数据,这个事务还没结束,接着另外一个事务插入了一些数据,在随后的查询中,第一个事务读取到的数据就会比原本读取到的多,就好像发生了幻觉一样,所以称为幻读
14. 事务隔离级别?
隔离级别 |
脏读 |
不可重复读 |
幻读 |
读取未提交 |
√ |
√ |
√ |
读取已提交 |
× |
√ |
√ |
可重复读 |
× |
× |
√ |
可串行化 |
× |
× |
× |
15. MySQL数据库的共享锁和排他锁
MySQL数据库的锁,按照作用范围划分为: 行级锁、页级锁和表级锁,行级锁是锁定粒度最细的一种锁,能大大减少数据库操作的冲突。行级锁又分为共享锁和排他锁两种
共享锁(Share Lock)
又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。
如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据
用法
SELECT … LOCK IN SHARE MODE;
排他锁(Exclusive Lock)
排他锁又称写锁、独占锁,如果事务T对数据A加上排他锁后,则其他事务不能再对A加任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。
用法
SELECT … FOR UPDATE;
Redis
1. Redis的五大类型介绍?
String(字符串)
string 是 redis 最基本的类型,一个 key 对应一个 value。string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。
应用场景:
String是最常用的一种数据类型,普通的key/value存储都可以归为此类,value其实不仅是String,也可以是数字:比如想知道什么时候封锁一个IP地址(访问超过几次)。
Hash(哈希)
Redis hash 是一个键值(key=>value)对集合。
Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
使用场景:存储、读取、修改用户属性
我们简单举个实例来描述下Hash的应用场景,比如我们要存储一个用户信息对象数据,包含以下信息: 用户ID,为查找的key,
存储的value用户对象包含姓名name,年龄age,生日birthday 等信息, 如果用普通的key/value结构来存储,主要有以下2种存储方式:
第一种方式将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储,
如:set u001 "李三,18,20010101"
这种方式的缺点是,增加了序列化/反序列化的开销,并且在需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题。
第二种方法是这个用户信息对象有多少成员就存成多少个key-value对儿,用用户ID+对应属性的名称作为唯一标识来取得对应属性的值,
如:mset user:001:name "李三 "user:001:age18 user:001:birthday "20010101" 虽然省去了序列化开销和并发问题,但是用户ID为重复存储,如果存在大量这样的数据,内存浪费还是非常可观的。
那么Redis提供的Hash很好的解决了这个问题。
List(列表)
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
应用场景:
Redis list的应用场景非常多,也是Redis最重要的数据结构之一。
我们可以轻松地实现最新消息排行等功能。
Lists的另一个应用就是消息队列,可以利用Lists的PUSH操作,将任务存在Lists中,然后工作线程再用POP操作将任务取出进行执行。
Set(集合)
Redis的Set是string类型的无序集合。
使用场景:1.共同好友、二度好友
2. 利用唯一性,可以统计访问网站的所有独立 IP
Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
比如在微博应用中,每个人的好友存在一个集合(set)中,这样求两个人的共同好友的操作,可能就只需要用求交集命令即可。
Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实
实现方式:
set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。
zset(sorted set:有序集合)
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
使用场景:
1.带有权重的元素,比如一个游戏的用户得分排行榜
2.比较复杂的数据结构,一般用到的场景不算太多
2. 什么是缓存击穿,缓存穿透,缓存雪崩?
1、缓存穿透
缓存穿透:指在redis缓存中不存在数据,这个时候只能去访问持久层数据库,当用户很多时,缓存都没有命中就会照成很大压力
解决方案 :
(1)布隆过滤器(对可能查询的数据先用hash存储)
(2)缓存空对象:在没有的数据中存一个空,而这些空的对象会设置一个有效期)
2、缓存击穿
缓存击穿:指在同一个时间内访问一个请求的请求数过多,而在这个时候缓存某个key失效了,这个时候就会冲向数据库照成缓存击穿
解决方案:
(1)设置缓存永远不过期
(2)加互斥锁,使用分布式锁,保证每个key只有一个线程去查询后端服务,而其他线程为等待状态。这种模式将压力转到了分布式锁上
3、缓存雪崩
缓存雪崩:在某个时间段,缓存集体过期、redis宕机
解决方案:给key的失效时间设置为随机时间,避免集体过期;双缓存;加互斥锁
3. 什么是热点key
热点Key产生的原因
1、用户消费的数据远大于生产的数据
2、请求分片集中,超过单Server的性能极限
热点Key问题的危害
1、流量集中,达到物理网卡上限
2、请求过多,缓存分片服务被打垮。
3、DB 击穿,引起业务雪崩。
热点key的发现
1、凭借业务经验,进行预估哪些是热key
2、在客户端进行收集
3、在Proxy层做收集'
4、用redis自带命令 monitor命令:该命令可以实时抓取出redis服务器接收到的命令,然后写代码统计出热key是啥。当然,也有现成的分析工具可以给你使用,比如redis-faina。但是该命令在高并发的条件下,有内存增暴增的隐患,还会降低redis的性能。
hotkeys参数:redis 4.0.3提供了redis-cli的热点key发现功能,执行redis-cli时加上–hotkeys选项即可。但是该参数在执行的时候,如果key比较多,执行起来比较慢。
5、自己抓包评估
热点Key的解决方案
1、利用二级缓存
2、读写分离
4. Redis的持久机制有那些?
RDB(Redis DataBase)持久化
Redis 默认采用的持久化机制
以 快照 的形式将进程数据持久化到硬盘中
会生成一个经过 压缩 的二进制文件(文件后缀为 .rdb)
文件内部存储了 各个数据库的 键值对数据
优点:生成的二进制文件体积小,恢复数据的速度快
缺点:bgsave 命令每次执行时都会 创建子进程(属于重量级操作),资源消耗大
AOF(Append Only File)持久化
目前 Redis 持久化 的主流方式(解决了数据持久化的实时性)
以 独立日志 的方式,记录了每次写入的命令
生成一个 文本协议格式 的 文本文件(文件后缀为 “.aof”)
优点:相较 RDB(一旦 内存掉电,存在于内存的数据全部丢失),AOF 丢失数据的量更小,而通过 everysec 选项,用户可以将数据丢失的时间窗口限制在1秒之内。
缺点:
AOF文件 存储的是 协议格式文本,其体积 相较于 RDB 的二进制文件 大很多
当需要恢复较多数据时,使用 AOF文件 恢复的速度会比较慢(原因在于:需要重新执行每一条命令)
AOF 在进行重写时也需要创建 子进程,当数据量较大时将占用较多资源,同时会导致服务器处于短暂阻塞状态
RabbitMq
1. RabbitMQ是什么?
RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,而群集和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。
2. RabbitMQ特点?
可靠性: RabbitMQ使用一些机制来保证可靠性, 如持久化、传输确认及发布确认等。
灵活的路由 : 在消息进入队列之前,通过交换器来路由消息。对于典型的路由功能, RabbitMQ 己经提供了一些内置的交换器来实现。针对更复杂的路由功能,可以将多个 交换器绑定在一起, 也可以通过插件机制来实现自己的交换器。
扩展性: 多个RabbitMQ节点可以组成一个集群,也可以根据实际业务情况动态地扩展 集群中节点。
高可用性 : 队列可以在集群中的机器上设置镜像,使得在部分节点出现问题的情况下队 列仍然可用。
多种协议: RabbitMQ除了原生支持AMQP协议,还支持STOMP, MQTT等多种消息 中间件协议。
多语言客户端 :RabbitMQ 几乎支持所有常用语言,比如 Java、 Python、 Ruby、 PHP、 C#、 JavaScript 等。
管理界面 : RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息、集 群中的节点等。
令插件机制: RabbitMQ 提供了许多插件 , 以实现从多方面进行扩展,当然也可以编写自 己的插件。
3. AMQP是什么?
RabbitMQ就是 AMQP 协议的 Erlang 的实现(当然 RabbitMQ 还支持 STOMP2、 MQTT3 等协议 ) AMQP 的模型架构 和 RabbitMQ 的模型架构是一样的,生产者将消息发送给交换器,交换器和队列绑定 。
RabbitMQ 中的交换器、交换器类型、队列、绑定、路由键等都是遵循的 AMQP 协议中相 应的概念。目前 RabbitMQ 最新版本默认支持的是 AMQP 0-9-1。
4. AMQP协议3层?
Module Layer:协议最高层,主要定义了一些客户端调用的命令,客户端可以用这些命令实现自己的业务逻辑。
Session Layer:中间层,主要负责客户端命令发送给服务器,再将服务端应答返回客户端,提供可靠性同步机制和错误处理。
TransportLayer:最底层,主要传输二进制数据流,提供帧的处理、信道服用、错误检测和数据表示等。
5. AMQP模型的几大组件?
交换器 (Exchange):消息代理服务器中用于把消息路由到队列的组件。
队列 (Queue):用来存储消息的数据结构,位于硬盘或内存中。
绑定 (Binding): 一套规则,告知交换器消息应该将消息投递给哪个队列。
6. 生产者Producer?
消息生产者,就是投递消息的一方。
消息一般包含两个部分:消息体(payload)和标签(Label)。
7. 消费者Consumer?
消费消息,也就是接收消息的一方。
消费者连接到RabbitMQ服务器,并订阅到队列上。消费消息时只消费消息体,丢弃标签。
8. Broker服务节点?
Broker可以看做RabbitMQ的服务节点。一般情况下一个Broker可以看做一个RabbitMQ服务器。
9. Queue队列?
Queue:RabbitMQ的内部对象,用于存储消息。多个消费者可以订阅同一队列,这时队列中的消息会被平摊(轮询)给多个消费者进行处理。
10. Exchange交换器?
Exchange:生产者将消息发送到交换器,有交换器将消息路由到一个或者多个队列中。当路由不到时,或返回给生产者或直接丢弃。
11. RoutingKey路由键?
生产者将消息发送给交换器的时候,会指定一个RoutingKey,用来指定这个消息的路由规则,这个RoutingKey需要与交换器类型和绑定键(BindingKey)联合使用才能最终生效。
12. Binding绑定?
通过绑定将交换器和队列关联起来,一般会指定一个BindingKey,这样RabbitMq就知道如何正确路由消息到队列了。
13. 交换器4种类型?
主要有以下4种。
fanout:把所有发送到该交换器的消息路由到所有与该交换器绑定的队列中。
direct:把消息路由到BindingKey和RoutingKey完全匹配的队列中。
topic:
匹配规则:
RoutingKey 为一个 点号'.': 分隔的字符串。 比如: java.xiaoka.show
BindingKey和RoutingKey一样也是点号“.“分隔的字符串。
BindingKey可使用 * 和 # 用于做模糊匹配,*匹配一个单词,#匹配多个或者0个
headers:不依赖路由键匹配规则路由消息。是根据发送消息内容中的headers属性进行匹配。性能差,基本用不到。
14. 生产者消息运转?
1.Producer先连接到Broker,建立连接Connection,开启一个信道(Channel)。
2.Producer声明一个交换器并设置好相关属性。
3.Producer声明一个队列并设置好相关属性。
4.Producer通过路由键将交换器和队列绑定起来。
5.Producer发送消息到Broker,其中包含路由键、交换器等信息。
6.相应的交换器根据接收到的路由键查找匹配的队列。
7.如果找到,将消息存入对应的队列,如果没有找到,会根据生产者的配置丢弃或者退回给生产者。
8.关闭信道。
9.管理连接。
15. 消费者接收消息过程?
1.Producer先连接到Broker,建立连接Connection,开启一个信道(Channel)。
2.向Broker请求消费响应的队列中消息,可能会设置响应的回调函数。
3.等待Broker回应并投递相应队列中的消息,接收消息。
4.消费者确认收到的消息,ack。
5.RabbitMq从队列中删除已经确定的消息。
6.关闭信道。
7.关闭连接。
16. 交换器无法根据自身类型和路由键找到符合条件队列时,有哪些处理?
mandatory :true 返回消息给生产者。
mandatory: false 直接丢弃。
17. 死信队列?
DLX,全称为 Dead-Letter-Exchange,死信交换器,死信邮箱。当消息在一个队列中变成死信 (dead message) 之后,它能被重新被发送到另一个交换器中,这个交换器就是 DLX,绑定 DLX 的队列就称之为死信队列。
18. 导致的死信的几种原因?
消息被拒(Basic.Reject /Basic.Nack) 且 requeue = false。
消息TTL过期。
队列满了,无法再添加。
19. 延迟队列?
存储对应的延迟消息,指当消息被发送以后,并不想让消费者立刻拿到消息,而是等待特定时间后,消费者才能拿到这个消息进行消费。
20. 优先级队列?
优先级高的队列会先被消费。
可以通过x-max-priority参数来实现。
当消费速度大于生产速度且Broker没有堆积的情况下,优先级显得没有意义。
21. 事务机制?
RabbitMQ 客户端中与事务机制相关的方法有三个:
channel.txSelect 用于将当前的信道设置成事务模式。
channel . txCommit 用于提交事务 。
channel . txRollback 用于事务回滚,如果在事务提交执行之前由于 RabbitMQ 异常崩溃或者其他原因抛出异常,通过txRollback来回滚。
22. 发送确认机制?
生产者把信道设置为confirm确认模式,设置后,所有再改信道发布的消息都会被指定一个唯一的ID,一旦消息被投递到所有匹配的队列之后,RabbitMQ就会发送一个确认(Basic.Ack)给生产者(包含消息的唯一ID),这样生产者就知道消息到达对应的目的地了。
23. 消费者获取消息的方式?
推
拉
24. 消费者某些原因无法处理当前接受的消息如何来拒绝?
channel .basicNack
channel .basicReject
25. 消息传输保证层级?
At most once:最多一次。消息可能会丢失,单不会重复传输。
At least once:最少一次。消息觉不会丢失,但可能会重复传输。
Exactly once: 恰好一次,每条消息肯定仅传输一次。
26. vhost?
每一个RabbitMQ服务器都能创建虚拟的消息服务器,也叫虚拟主机(virtual host),简称vhost。
默认为“/”。
27. 集群中的节点类型?
内存节点:ram,将变更写入内存。
磁盘节点:disc,磁盘写入操作。
RabbitMQ要求最少有一个磁盘节点。
28. 队列结构?
通常由以下两部分组成?
rabbit_amqqueue_process :负责协议相关的消息处理,即接收生产者发布的消息、向消费者交付消息、处理消息的确认(包括生产端的 confirm 和消费端的 ack) 等。
backing_queue:是消息存储的具体形式和引擎,并向 rabbit amqqueue process 提供相关的接口以供调用。
29. RabbitMQ中消息可能有的几种状态?
alpha: 消息内容(包括消息体、属性和 headers) 和消息索引都存储在内存中 。
beta: 消息内容保存在磁盘中,消息索引保存在内存中。
gamma: 消息内容保存在磁盘中,消息索引在磁盘和内存中都有 。
delta: 消息内容和索引都在磁盘中 。
EF
1. 客户端与服务器评估?
作为一般规则,Entity Framework Core 会尝试尽可能全面地评估服务器上的查询。 EF Core 将查询的一部分转换为可在客户端评估的参数。 系统将查询的其余部分(及生成的参数)提供给数据库提供程序,以确定要在服务器上评估的等效数据库查询。 EF Core 支持在顶级投影中进行部分客户端评估(基本上为最后一次调用 Select())。 如果查询中的顶级投影无法转换为服务器,EF Core 将从服务器中提取任何所需的数据,并在客户端上评估查询的其余部分。 如果 EF Core 在顶级投影之外的任何位置检测到不能转换为服务器的表达式,则会引发运行时异常。 请参阅查询工作原理,了解 EF Core 如何确定哪些表达式无法转换为服务器。
备注
在 3.0 版之前,Entity Framework Core 支持在查询中的任何位置进行客户端评估。 有关详细信息,请参阅历史版本部分。
提示
可在 GitHub 上查看此文章的示例。
顶级投影中的客户端评估
在下面的示例中,一个辅助方法用于标准化从 SQL Server 数据库中返回的博客的 URL。 由于 SQL Server 提供程序不了解此方法的实现方式,因此无法将其转换为 SQL。 查询的所有其余部分是在数据库中评估的,但通过此方法传递返回的 URL 却是在客户端上完成。
C#
var blogs = context.Blogs
.OrderByDescending(blog => blog.Rating)
.Select(
blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog.Url) })
.ToList();
C#
public static string StandardizeUrl(string url)
{
url = url.ToLower();
if (!url.StartsWith("http://"))
{
url = string.Concat("http://", url);
}
return url;
}
不支持的客户端评估
尽管客户端评估非常有用,但有时会减弱性能。 请看以下查询,其中的 where 筛选器现已使用辅助方法。 由于数据库中不能应用筛选器,因此需要将所有数据提取到内存中,以便在客户端上应用筛选器。 根据服务器上的筛选器和数据量,客户端评估可能会减弱性能。 因此 Entity Framework Core 会阻止此类客户端评估,并引发运行时异常。
C#
var blogs = context.Blogs
.Where(blog => StandardizeUrl(blog.Url).Contains("dotnet"))
.ToList();
显式客户端评估
在某些情况下,可能需要以显式方式强制进行客户端评估,如下所示
由于数据量小,因此在进行客户端评估时才不会大幅减弱性能。
所用的 LINQ 运算符不会进行任何服务器端转换。
在这种情况下,通过调用 AsEnumerable 或 ToList 等方法(若为异步,则调用 AsAsyncEnumerable 或 ToListAsync),以显式方式选择进行客户端评估。 使用 AsEnumerable 将对结果进行流式传输,但使用 ToList 将通过创建列表来进行缓冲,因此也会占用额外的内存。 但如果枚举多次,则将结果存储到列表中可以带来更大的帮助,因为只有一个对数据库的查询。 根据具体的使用情况,你应该评估哪种方法更适合。
C#
var blogs = context.Blogs
.AsEnumerable()
.Where(blog => StandardizeUrl(blog.Url).Contains("dotnet"))
.ToList();
提示
如果你正在使用 AsAsyncEnumerable 并希望在客户端进一步编写查询,可以使用用于定义异步枚举运算符的 System.Interactive.Async 库。 有关详细信息,请参阅客户端 linq 运算符。
客户端评估中潜在的内存泄漏
由于查询转换和编译的开销高昂,因此 EF Core 会缓存已编译的查询计划。 缓存的委托在对顶级投影进行客户端评估时可能会使用客户端代码。 EF Core 为树型结构中客户端评估的部分生成参数,并通过替换参数值重用查询计划。 但表达式树中的某些常数无法转换为参数。 如果缓存的委托包含此类常数,则无法将这些对象垃圾回收,因为它们仍被引用。 如果此类对象包含 DbContext 或其中的其他服务,则会导致应用的内存使用量逐渐增多。 此行为通常是内存泄漏的标志。 只要遇到的常数为不能使用当前数据库提供程序映射的类型,EF Core 就会引发异常。 常见原因及其解决方案如下所示:
使用实例方法:在客户端投影中使用实例方法时,表达式树包含实例的常数。 如果你的方法不使用该实例中的任何数据,请考虑将该方法设为静态方法。 如果需要方法主体中的实例数据,则将特定数据作为实参传递给方法。
将常数实参传递给方法:这种情况通常是由于在客户端方法的实参中使用 this 引起的。 请考虑将实参拆分为多个标量实参,可由数据库提供程序进行映射。
其他常数:如果在任何其他情况下都出现常数,则可以评估在处理过程中是否需要该常数。 如果必须具有常数,或者如果无法使用上述情况中的解决方案,则创建本地变量来存储值,并在查询中使用局部变量。 EF Core 会将局部变量转换为形参。
早期版本
以下部分适用于 3.0 以前的 EF Core 版本。
旧的 EF Core 版本支持在查询的任何部分中进行客户端评估,而不仅仅是顶级投影。 因此,与不支持的客户评估部分下发布的查询类似的查询可以正常工作。 由于此行为可能引起不易觉察的性能问题,EF Core 记录了客户端评估警告。 有关如何查看日志记录输出的详细信息,请参阅日志记录。
(可选)借助 EF Core,你可以将默认行为更改为在执行客户端评估时引发异常或不执行任何操作(在投影中除外)。 引发异常的行为会使其类似于 3.0 中的行为。 若要更改该行为,你需要在设置上下文选项时配置警告。上下文选项一般在 DbContext.OnConfiguring 中设置,如果使用 ASP.NET Core,则在 Startup.cs 中设置。
C#
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying;Trusted_Connection=True;")
.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
}
2. 跟踪与非跟踪查询
跟踪行为决定了 Entity Framework Core 是否将有关实体实例的信息保留在其更改跟踪器中。 如果已跟踪某个实体,则该实体中检测到的任何更改都会在 SaveChanges() 期间永久保存到数据库。 EF Core 还将修复跟踪查询结果中的实体与更改跟踪器中的实体之间的导航属性。
3. 拆分查询
单个查询
在关系数据库中,所有相关实体通过在单个查询中引入 JOIN 来加载。
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId], [p].[PostId]
如果典型博客有多篇相关文章,这些文章对应的行会复制博客的信息。 这种复制会导致所谓的“笛卡尔爆炸”问题发生。 随着加载更多的一对多关系,重复的数据量可能会增长,并对应用程序性能产生负面影响。
拆分查询
此功能是在 EF Core 5.0 中引入的,仅在使用 Include 时才有效。 在不使用 Include 加载投影中的相关数据的情况下,EF Core 6.0 添加了对拆分查询的支持。
通过 EF,可以指定应将给定 LINQ 查询拆分为多个 SQL 查询。 与 JOIN 不同,拆分查询为包含的每个集合导航生成额外的 SQL 查询:
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.AsSplitQuery()
.ToList();
}
这会生成以下 SQL:
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url]
FROM [Blogs] AS [b]
ORDER BY [b].[BlogId]
SELECT [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title], [b].[BlogId]
FROM [Blogs] AS [b]
INNER JOIN [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId]
警告
将拆分查询与 Skip/Take 配合使用时,请特别注意使查询排序完全唯一;不这样做可能会导致返回不正确的数据。 例如,如果结果仅按日期排序,但可能有多个具有相同日期的结果,则每个拆分查询都可以从数据库获取不同的结果。 按日期和 ID(或任何其他唯一属性或属性组合进行排序)使排序完全唯一,并避免此问题。 请注意,关系数据库默认不应用任何排序,即使在主键上也是如此。
备注
一对一相关实体始终都是通过 JOIN 在同一查询中进行加载的,因为这对性能没有影响。
全局启用拆分查询
还可以将拆分查询配置为应用程序上下文的默认查询:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=EFQuerying;Trusted_Connection=True",
o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));
}
将拆分查询配置为默认查询后,仍然可以将特定查询配置为以单个查询的形式执行
using (var context = new SplitQueriesBloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.AsSingleQuery()
.ToList();
}
如果没有任何配置,默认情况下,EF Core 使用单个查询模式。 由于这可能会导致性能问题,因此,只要满足以下条件,EF Core 就会生成警告:
EF Core 检测到查询加载了多个集合。
用户未全局配置查询拆分模式。
用户未在查询上使用 AsSingleQuery/AsSplitQuery 运算符。
若要关闭警告,请全局配置查询拆分模式,或在查询级别将其配置为适当的值。
拆分查询的特征
虽然拆分查询避免了与 JOIN 和笛卡尔爆炸相关的性能问题,但它也有一些缺点:
虽然大多数数据库对单个查询保证数据一致性,但对多个查询不存在这样的保证。 如果在执行查询时同时更新数据库,生成的数据可能会不一致。 这可以通过将查询包装在可序列化或快照事务中来缓解,尽管这样做本身可能会产生性能问题。 有关详细信息,请参见数据库器文档。
当前,每个查询都意味着对数据库进行一次额外的网络往返。 多次网络往返会降低性能,尤其是在数据库延迟很高的情况下(例如云服务)。
虽然有些数据库(带有 MARS 的 SQL Server、Sqlite)允许同时使用多个查询的结果,但大多数数据库在任何给定时间点只允许一个查询处于活动状态。 因此,在执行以后的查询之前,必须先在应用程序的内存中缓冲先前查询的所有结果,这将增加内存需求。
遗憾的是,没有一种加载相关实体的策略可以适用于所有情况。 请仔细考虑单个查询和拆分查询的优缺点,以便选择能够满足你需求的策略。
4. 配置模型的方式有几种分别是什么
- fluent API 配置模型
- 注释 配置模型
可在派生上下文中替代 OnModelCreating 方法,并使用 ModelBuilder API 来配置模型。 此配置方法最为有效,并可在不修改实体类的情况下指定配置。 Fluent API 配置具有最高优先级,并将替代约定和数据注释。
using Microsoft.EntityFrameworkCore;
namespace EFModeling.EntityProperties.FluentAPI.Required;
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
#region Required
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.IsRequired();
}
#endregion
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
使用数据注释来配置模型
也可将特性(称为数据注释)应用于类和属性。 数据注释会替代约定,但会被 Fluent API 配置替代。
5. 排除一个属性用fluent API怎么配置模型?用注释怎么配置模型
Fluent API
public class BlogEntityTypeConfiguration : IEntityTypeConfiguration<Blog>
{
public void Configure(EntityTypeBuilder<Blog> builder)
{
builder
.Property(b => b.Url)
.IsRequired();
}
}
注释
[NotMapped]
public string Url { get; set; }
6. 并发标记
使用配置为并发标记的属性实现乐观并发控制。
注释配置
public class Person
{
public int PersonId { get; set; }
[ConcurrencyCheck]
public string LastName { get; set; }
public string FirstName { get; set; }
}
Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.Property(p => p.LastName)
.IsConcurrencyToken();
}
Timestamp/rowversion 是每次插入行或更新行时数据库自动生成新值的属性。 该属性也被视为并发令牌,确保在查询后要更新的行发生更改时得到异常。 具体的详细信息取决于所使用的数据库提供程序;对于 SQL Server,通常使用 byte[] 属性,该属性将设置为数据库中的 ROWVERSION 列。
可以将属性配置为 timestamp/rowversion,如下所示:
注释
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; }
}
Fluent API
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(p => p.Timestamp)
.IsRowVersion();
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public byte[] Timestamp { get; set; }
}
微服务
1. 说说微服务架构的优势?
独立开发 所有微服务都可以根据各自的功能轻松开发
独立部署 根据他们所提供的服务,可以在任何应用中单独部署
故障隔离 即使应用中的一个服务不起作用,系统仍然继续运行
混合技术栈 可以用不同的语言和技术来构建同一应用程序的不同服务
粒度缩放 各个组件可根据需要进行扩展,无需将所有组件融合到一起
2. 你对微服务是怎么理解的?
微服务,又名微服务架构,是一种架构风格,它将应用构建为一个小型自治服务的集合,以业务领域为模型。
通俗地说,就像蜜蜂通过对蜡制的等边六角形单元来构建它们的蜂巢。
他们最初从使用各种材料的小单元开始,一点点的搭建出一个大型蜂巢。
这些小单元组成坚固的结构,将蜂窝的特定部分固定在一起。
这里,每个小单元都独立于另一个,但它也与其他小单元相关。
这意味着对一个小单元的损害不会损害其他的单元,因此,蜜蜂可以在不影响完整蜂巢的情况下重建这些单元。
3. 微服务有哪些特点?
解耦(Decoupling) - 系统内的服务很大程度上是分离的。因此整个应用可以被轻松构建、修改和扩展
组件化(Componentization) - 微服务被视为可以被轻松替换和升级的独立组件
业务能力(Business Capabilities) - 微服务非常简单,专注于单一功能
自治(Autonomy) - 开发人员和团队可以相互独立工作,从而提高效率
持续交付(ContinousDelivery) - 允许频繁发版,通过系统自动化完成对软件的创建、测试和审核,
责任(Responsibility) - 微服务不把程序作为项目去关注。相反,他们将程序视为自己负责的产品
分散治理(Decentralized Governance) - 重点是用正确的工具去做正确的事。这意味着没有任何标准化模式或着技术模式。开发人员可以自由选择最合适的工具来解决自己的问题
敏捷性(Agility) - 微服务支持敏捷开发。任何新功能都可以快速开发并被再次丢弃
4. 设计微服务的最佳实践是什么?
为每个微服务分开数据存储
将代码保持在类似的成熟度等级上
为每个微服务进行单独的构建
部署到容器中
将服务器视为无状态的
5. 什么是领域驱动设计(DDD)?
专注于核心领域逻辑
在模型上找到综合的设计
不断与领域专家合作,改进应用程序模型并解决与领域相关的问题
6. 为什么需要域驱动设计(DDD)?
映射领域
降低复杂性
可测试性
可维护性
知识丰富的设计
将业务和服务结合在一起
上下文集中
通用语言
7. 什么是内聚?
内聚是一个模块内部各元素之间相关联程度的度量
8. 什么是耦合?
组件之间依赖关系强度的度量被称为耦合。好的设计总是高内聚和低耦合的。
9. 什么是REST/RESTful ?它的用途是什么?
Representational State Transfer(REST)/ RESTful (表述性状态转移)是一种帮助计算机系统通过 Internet 进行通信的架构风格。这使得微服务更容易理解和实现。
微服务可以用 RESTful API 来实现,当然也可以不用,但是用 RESTful API 去构建松散耦合的微服务总是更容易些。
其他
- 您对你未来3-5年的规划是什么呢?
开始您的表演
- 您对你的期望薪酬是多少呢?
说出您的想法
- 您最近上家公司的离职原因是什么呢?
如实回答
- 您目前在职还是离职状态
如实回答
- 您最快多久能到岗
如实回答
职员关心
1. 薪酬能给多少
2.薪酬发放日期
3.上班时间下班时间午休时间
4.工作是否外派 外包?
5.工资组成成分
6.年终绩效奖励
7.加班折算 (调休1:1 ,1:0.5) (折算工资 1:1?)
8.工作使用技术,以及接触项目,以及业务介绍
9.是否出差(如果出差以及基本事项)
10.每年涨薪酬标准?
11.是否签订保密协议(是否发放保密费用)
12.五险一金?六险一金 是否入职既购买
13.试用期多久?试用80%薪酬?
14.公司还有其他那些福利
15.是否入职就签订劳动合同(包含重点 薪酬 ,加班请假规定,年终绩效等等)
16.公司团队组成?技术团队组成? 有多少个后端 高级多少 中级多少 前端有多少 架构师几个 ? DBA有? 数据分析师有? 测试有? 运维有?