精华内容
下载资源
问答
  • 系统监控一直是项目完整性的一要素,“不让没有监控的系统上线”,这条准则也逐渐得到越来越多的人的认可。如果一系统监控缺失,我们就无法知道系统的运行状态,以及业务的各个方面的情况,甚至系统...

    系统监控一直是项目完整性的一个要素,“不让没有监控的系统上线”,这条准则也逐渐得到越来越多的人的认可。如果一个系统监控缺失,我们就无法知道系统的运行状态,以及业务的各个方面的情况,甚至系统出现宕机或者重大故障也不得而知,以至于造成重大损失。

    爱奇艺乐道中台是由爱奇艺内容中台团队建设的集视频、音频、字幕、图片等内容的全流程生产、发布及运营的中台服务。随着业务发展及微服务化的逐渐深入,系统项目越来越多,目前已有微服务100+,需要维护内容也越来越多,链路越来越长,现有的监控手段已不能满足业务发展的需要,涌现出更多更迫切的监控需求,如:

    1、高效及时的发现、定位系统异常问题

    系统出现异常时需要立刻感知,在业务反馈之前发现问题,定位问题需要更加高效,可视化,不能仅去服务器查日志定位问题,这样通常需要花费很长时间才能定位原因,甚至如果日志过多还不能准确定位。

    2、上线过程可监测,出现问题第一时间回滚

    每次系统发布上线都是一次bug降生的窗口期,相当比例线上问题都是上线导致或者引入的,因为一个稳定的服务,你不去更新系统代码,正常情况下不会自发的产生问题。我们需要可监测的上线过程,及时修复问题。

    3、全面准确的度量系统服务、接口、数据库等性能

    我们需要更加全面和方便的监测整个系统对外提供的服务接口和系统依赖的外部服务接口性能,QPS、慢查询等其他性能指标,性能如果有潜在问题可能在关键时候,如流量突增,导致数据库连接池打满、长事务、拖垮整个网关等重大故障。

    4、更加及时和稳定的监测服务健康情况

    虽然目前部署的服务都是双份或者多备份部署,可用性有保证,但是还是往往会出现其中部分服务出现宕机等情况,我们需要第一时间感知到这种问题,并具有持续的稳定监测。

    5、及时监测机器(容器)性能变化

    当系统遇到突发流量或者攻击等原因时很可能导致连接池打满、OOM,数据库打满,CPU、内存、磁盘打满等基础问题,这些都会导致重大事故,无法及时的监控到,就无法及时的处理,导致重大线上故障。

    6、更加完善清晰的业务监控

    以上叙述了系统监控方面的迫切需求,在业务方面如果没有监控,如节目生产数量、成功率、时效等,那么整体业务运行情况对于维护者而言就是一个黑盒,这就会陷入一个被动局面,业务的发展是技术保障的目标,不知道目标发展情况,就会让许多工作失去意义。

    在业务量较少,系统较少,微服务较少的时候,我们对于监控系统的需求越少,随着业务、系统爆炸性的发展就会维护起来越来越吃力,我们需要建立完备的监控体系去补齐这个短板,对各个服务、业务心中有数,为业务快速发展提供保障。

    监控内容及技术选型

    经过梳理,目前我们服务系统需要监控的具体内容有:

    1、机器监控

    最基础的监控,主要监控承载各个系统的虚拟机或者容器的运行情况,主要有CPU、内存、磁盘、网络等大的方面,具体指标如:cpu.busy、cpu.idle、cpu.load.5min.per.core、mem.memfree、net.if.in.bytes、df.statistics.used.percent等。

    2、系统监控

    各个系统所有接口、服务的监控,主要如服务状态、qps、接口性能、成功率、错误日志等。

    具体如:服务健康情况、服务api接口qps、服务api接口性能、成功率、集群中各个机器qps、集群中各个机器访问量、整个集群qps压力、整个系统异常日志趋势等数据。

    3、业务监控

    根据各个服务承载的具体业务,建立业务数据大盘及相对应的监测报警策略,方便观测业务发展情况、系统运行情况、以及为业务发展决策提供参考和帮助。

    在本着不重复造轮子的原则下,首先我们调研了公司内部现有基础监控系统的功能、接入方式、侵入性、优缺点等,由于公司内部监控系统功能比较分散、单一,无法满足我们所需的全面监控需求,所以我们倾向于自建,于是调研了常见的开源监控系统:

    1、OpenFalcon

    OpenFalcon是小米开源的监控系统,能够提供丰富的基础监控指标。

    优势:易于接入,基本无入侵,可自定义上报数据

    劣势:主要监控机器指标,系统监控不足

    2、Prometheus

    Prometheus是由前谷歌员工2015年正式发布的开源监控系统。它和OpenFalcon最大不同在于:数据采集是基于Pull模式的,而不是Push模式,并且架构非常简单。

    优势:查询引擎强大,支持PromQL,可以对数据做各种实时计算。

    劣势:不易用,学习成本高,功能不够完善,基本需要搭配Grafana创建仪表盘以及查看指标。

    3、开源监控系统CAT

    CAT 是美团点评开源的实时应用监控平台,提供了比较全面的实时监控告警服务。

    优势:监控功能强大,基本上可以覆盖各种监控场景

    劣势:接入成本较高、对业务代码侵入较大

    经过多方面比对以及结合我们系统业务的监控需求,最重要的是监控功能的全面、稳定、成熟的UI、报表、告警,所以我们最后选型CAT作为我们各个系统微服务的监控系统。

    LEDAO-CAT监控系统的落地

    1、部署与迭代

    CAT的部署最初是以GitHub开源代码以及相关文档进行最小化部署和试用,最初用于虚机上的少量业务系统,通过实验发现试用效果很好,满足我们对于监控系统最初的需求,随后我们对CAT进行了若干次的升级改造,来逐渐优化以适用乐道中台的业务和环境。

    目前整个乐道中台部署的CAT监控系统分别服务于海外、中国大陆,每个区域都部署了一个集群服务于对应的业务系统,如图1所示,其中目前以中国大陆的集群服务最多,国内集群微服务接入量100+、TPS 1w+,每日处理数据量1.5TB 左右,业务系统与CAT监控系统的交互如图2所示????

    图 1 LEDAO-CAT部署

    图 2 交互流程

    其中,目前CAT支持的业务应用包括物理机、虚机、QAE容器等系统运行环境,服务通过引入ledao-cat-client包后进行简单的环境配置和监控配置即可接入监控系统,对相关的服务进行全方面的监控。

    主要流程是cat-client-proxy收集监控配置,与服务端建立连接,通过读取需要监控的项的配置,对要监控的数据进行上报,上传到CAT服务端后对于原始数据处理、聚合、告警等。

    2、升级和改造

    (1)CAT接入方式和监控埋点升级

    原生CAT在系统接入时的方式是在每各应用所在的虚机固定路径上配置client.xml,其中内容主要是服务端的连接地址和端口,这样就需要在服务部署的每台机器都去配置文件,运维成本极高,在公司无统一运维的情况下,接入和维护成本都很高,而且这种方式不适用于QAE容器部署。为了解决上述问题,我们对CAT接入配置模块进行了改造,使其支持三种方式的接入配置:传统xml文件,QAE环境变量,系统配置文件properties(xml),使其配置与宿主机解耦,只依赖于应用本身。

    另外,CAT监控是具有侵入性的监控系统,需要在监控的地方埋点上报数据进行监控,原生的埋点方式基本是切面方式,为了简化和细化监控埋点工作,我们开发了proxy代理包,方便用户接入,其中主要扩展的埋点方式为:传统切面、声明式注解(service、method、controller、dao),以及批量配置文件方式(properties),具体如图3所示????

    图 3 接入方式

    (2)新增CAT健康检查模块

    CAT的各项监控功能已经非常全面,但是却没有应用健康的监控,监控的方式是由客户端进行数据上报,当服务宕机时,业务不可用时往往就会停止数据上报,但是CAT目前无法检测到这种客户端宕机异常,所以我们对原生CAT进行了升级,加入cat-health健康检查模块到CAT系统中,通过CAT配置健康检查,以及客户端上报的应用信息,健康检查模块会定时从不同机房拨测应用来检测应用的健康状况,如果多次反复检测到应用异常,会根据一定的判定规则进行告警通知,通过新建健康检查模块填补了原生CAT监控的空白。具体如图4所示????

    图 4 健康检查

    (3)告警方式升级

    此外,在原有开源CAT告警方式基础上,结合爱奇艺自有的告警体系,我们将两者进行了融合对接,通过整合完备了整个监控系统的告警体系,支持以爱奇艺邮件、热聊、短信的方式进行对异常、健康、业务等各方面的告警发送,大大提高了告警信息的触达率,使异常实时可见,及时处理和恢复。

    图 5 告警升级

    实践成果

    通过对原生CAT的部署及相关改造升级,我们逐步建设了一套完整的乐道中台微服务监控体系,从机器指标监控、服务健康监控、系统异常监控、系统性能监控、慢查询监控等到相关业务监控,结合CAT强大的告警配置以及爱奇艺告警方式的多样,以及优化后的CAT快速接入及埋点方式,形成了从业务快速接入——监控快速埋点——告警配置——告警触达——告警处理一整条监控链路,完整的填补了监控体系的空白。

    其中具体是:

    1. CAT接入方式多样,对接成本低,一个新业务接入在5min之内可以完成。

    2. 结合cat-client-proxy依赖包,埋点配置几乎可以做到无入侵,而且整个配置快速高效。

    3. 微服务系统从硬件指标、健康情况、异常情况、性能、业务等能够被全方位的监控,几乎覆盖了所有的方面。

    4. 强大的告警配置能够实时推送系统异常信息,使问题被快速感知,及时处理。

    图 6 各监控指标图

    图 7 热聊告警

    图 8 配置接入

    从最小化部署实验,到功能升级改造,再到团队推广及同事认可,LEDAO-CAT逐步发展壮大,目前乐道中台整个团队统一使用LEDAO-CAT作为监控工具,目前分别部署了大陆和海外两大集群,保障线上服务稳定高效运行,为快速发展的业务保驾护航。

    总结展望

    监控系统一直是业务发展过程中不可或缺的重要一部分,对于服务稳定运行提供极其重要的保障作用,对于不同的业务系统当然也会有不同的与其适配的监控系统,目前在国内和国外,不同公司都提供了一些开源的监控服务,CAT作为一款优秀的开源监控系统,提供了非常全面和强大的监控功能,基本能够满足我们目前所有的监控需求。

    本文大概介绍了CAT在爱奇艺乐道中台的一次落地实践,主要介绍了我们监控的需求、解决方案、优化改造以及阶段性成果,虽然目前的功能大概满足我们的需求,但是还是有如下几点不足仍在研究,如分布式Transaction、业务监控大盘、与nacos的深度整合等,这些都是后续我们要做的方向,使整个监控体系更加完善,更快更好更新的支撑业务快速发展。

    也许你还想看

    移动端APM网络监控与优化实践

    爱奇艺微服务监控的探索与实践

     扫一扫下方二维码,更多精彩内容陪伴你!

    展开全文
  • 论文主要内容:1.利用DDQN做路径规划 2.利用卷积神经网络处理全局地图 3.考虑能量约束,任意位置起降 摄像头给出当前的覆盖率视图,GPS给出无人机的位置 覆盖问题利用三通道的二维网格图分析,设置三主要区域:...

    论文题目《UAV Coverage Path Planning under Varying Power Constraints using Deep Reinforcement Learning》
    论文主要内容:1.利用DDQN做路径规划
    2.利用卷积神经网络处理全局地图
    3.考虑能量约束,任意位置起降
    摄像头给出当前的覆盖率视图,GPS给出无人机的位置

    原理流程图

    覆盖问题利用三通道的二维网格图分析,设置三个主要区域:起降区,禁飞区,目标区;一个网格区域可以同时被设置为这三个区域中的一个或者多个,但是不能同时事起降区和禁飞区。
    飞行器位置使用独热码表示,它可以与三通道地图和覆盖网格以五通道输入网络的卷积层来编码。
    实验
    每次训练循环都有能量预算,每一个动作进行时,飞行器在当前位置无论是接受还是不接受都消耗一个单位的能量预算。
    飞行器的初始位置由一个固定地图,零初始化的覆盖网格和位置组成
    飞行器的摄像头可视范围设定为摄像头正下方3*3的网格区域,每一步动作后,地图都会标记覆盖网格中的可视范围
    利用三个不同的地图进行训练,评估覆盖率和训练次数
    训练地图

    蓝色为起降区,黄色时初始位置,白色时终止位置,绿色时目标区,红色是禁飞区,黑色表示未覆盖
    论文没有源码

    展开全文
  • cloud-square了,本篇咱们就来畅快实战,体验这spring官方带给我们的smart client 如标题所述,接下里咱们会将spring-cloud-square提供的三种client都编码体验,总的来说本篇由以下内容构成: 新建maven工程,名为...

    欢迎访问我的GitHub

    这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

    本篇概览

    • 前文《五分钟搞懂spring-cloud-square》详细介绍了什么是spring-cloud-square,以及三种实现类型的详细概念,爱动手的您已迫不及待想编码体验spring-cloud-square了,本篇咱们就来畅快实战,体验这个spring官方带给我们的smart client
    • 如标题所述,接下里咱们会将spring-cloud-square提供的三种client都编码体验,总的来说本篇由以下内容构成:
    1. 新建maven工程,名为spring-cloud-square-tutorials,这是本篇所有应用的父工程,库版本在此工程中统一管理;
    2. 创建子工程eureka,作为注册中心
    3. 创建子工程client,放一些公用的数据结构
    4. 创建子工程provider,身份是服务提供者,接下来的三个用到spring-cloud-square的子工程,都调用provider的服务
    5. 创建子工程consumer-okhttp,基于spring-cloud-square的okhttp能力做远程调用
    6. 创建子工程consumer-retrofit-okhttp,基于spring-cloud-square的retrofit + okhttp能力做远程调用
    7. 创建子工程consumer-retrofit-webflux,基于spring-cloud-square的retrofit + webflux能力做远程调用
    • 上述几个服务的关系如下图:
      在这里插入图片描述

    如何验证

    • 代码写完之后,如何验证功能是否符合预期呢?本篇采用单元测试的方式,consumer-okhttp、consumer-retrofit-okhttp、consumer-retrofit-webflux这三个子工程都有自己的单元测试代码,执行通过就意味着代码功能符合预期了

    源码下载

    • 本篇实战中的完整源码可在GitHub下载到,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos):
    名称链接备注
    项目主页https://github.com/zq2599/blog_demos该项目在GitHub上的主页
    git仓库地址(https)https://github.com/zq2599/blog_demos.git该项目源码的仓库地址,https协议
    git仓库地址(ssh)git@github.com:zq2599/blog_demos.git该项目源码的仓库地址,ssh协议
    • 这个git项目中有多个文件夹,本篇的源码在spring-cloud-square-tutorials文件夹下,如下图红框所示:
      在这里插入图片描述

    版本信息

    • 本篇实战涉及到的主要版本情况如下:
    1. JDK:1.8.0_291
    2. IDEA:2021.1.3 (Ultimate Edition)
    3. maven:3.8.1
    4. 操作系统:win10 64位
    5. springboot:2.4.4
    6. spring-cloud:2020.0.2
    7. spring-cloud-square:0.4.0-SNAPSHOT

    父工程spring-cloud-square-tutorials

    • 父工程名为spring-cloud-square-tutorials,其pom.xml如下,除了依赖库的版本在此统一管理,还要注意的是两个仓库的引入(https://repo.spring.io/snapshot和https://repo.spring.io/milestone),引入它们是因为spring-cloud-square还在孵化阶段,没有发布到maven中央仓库:
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.4.4</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <groupId>com.bolingcavalry</groupId>
        <artifactId>spring-cloud-square-tutorials</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
            <java.version>1.8</java.version>
            <spring-cloud.version>2020.0.2</spring-cloud.version>
            <square.dependency.version>0.4.0-SNAPSHOT</square.dependency.version>
        </properties>
    
        <packaging>pom</packaging>
        <description>Demo project for Spring Cloud Square Retrofit Web</description>
    
        <modules>
            <module>provider</module>
            <module>eureka</module>
            <module>consumer-okhttp</module>
            <module>client</module>
            <module>consumer-retrofit-okhttp</module>
            <module>consumer-retrofit-webflux</module>
        </modules>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
    
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
    
                <dependency>
                    <groupId>com.squareup.okhttp3</groupId>
                    <artifactId>okhttp</artifactId>
                    <version>3.14.9</version>
                    <scope>compile</scope>
                </dependency>
    
                <dependency>
                    <groupId>ch.qos.logback</groupId>
                    <artifactId>logback-classic</artifactId>
                    <version>1.1.7</version>
                </dependency>
    
                <dependency>
                    <groupId>org.projectlombok</groupId>
                    <artifactId>lombok</artifactId>
                    <version>1.16.16</version>
                </dependency>
    
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-square-okhttp</artifactId>
                    <version>${square.dependency.version}</version>
                </dependency>
    
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-square-retrofit</artifactId>
                    <version>${square.dependency.version}</version>
                </dependency>
    
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-boot-starter-webflux</artifactId>
                    <version>${square.dependency.version}</version>
                </dependency>
    
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-square-retrofit-webclient</artifactId>
                    <version>${square.dependency.version}</version>
                </dependency>
    
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <!--skip deploy (this is just a test module) -->
                    <artifactId>maven-deploy-plugin</artifactId>
                    <configuration>
                        <skip>true</skip>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
        <repositories>
            <repository>
                <id>spring-snapshots</id>
                <name>Spring Snapshots</name>
                <url>https://repo.spring.io/snapshot</url>
                <snapshots>
                    <enabled>true</enabled>
                </snapshots>
            </repository>
            <repository>
                <id>spring-milestones</id>
                <name>Spring Milestones</name>
                <url>https://repo.spring.io/milestone</url>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </repository>
        </repositories>
        <pluginRepositories>
            <pluginRepository>
                <id>spring-snapshots</id>
                <name>Spring Snapshots</name>
                <url>https://repo.spring.io/snapshot</url>
                <snapshots>
                    <enabled>true</enabled>
                </snapshots>
            </pluginRepository>
            <pluginRepository>
                <id>spring-milestones</id>
                <name>Spring Milestones</name>
                <url>https://repo.spring.io/milestone</url>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </pluginRepository>
        </pluginRepositories>
    </project>
    

    注册中心eureka

    • eureka应用并没有什么特别之处,pom.xml如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>spring-cloud-square-tutorials</artifactId>
            <groupId>com.bolingcavalry</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>eureka</artifactId>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <start-class>com.bolingcavalry.eureka.EurekaApplication</start-class>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            </dependency>
        </dependencies>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
    <!--                <version>Finchley.BUILD-SNAPSHOT</version>-->
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
    
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <!-- defined in spring-cloud-starter-parent pom (as documentation hint),
                        but needs to be repeated here -->
                    <configuration>
                        <requiresUnpack>
                            <dependency>
                                <groupId>com.netflix.eureka</groupId>
                                <artifactId>eureka-core</artifactId>
                            </dependency>
                            <dependency>
                                <groupId>com.netflix.eureka</groupId>
                                <artifactId>eureka-client</artifactId>
                            </dependency>
                        </requiresUnpack>
                    </configuration>
                </plugin>
    
                <plugin>
                    <!--skip deploy (this is just a test module) -->
                    <artifactId>maven-deploy-plugin</artifactId>
                    <configuration>
                        <skip>true</skip>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    
    • 中规中矩的配置文件application.yml,端口是8761,后面的应用也要保持一致:
    server:
      port: 8761
    
    spring:
      application:
        name: eureka
    
    eureka:
      client:
        registerWithEureka: false
        fetchRegistry: false
      server:
        waitTimeInMsWhenSyncEmpty: 0
    
    • 启动类EurekaApplication.java,记得用注解EnableEurekaServer开启eureka服务:
    package com.bolingcavalry.eureka;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
    
    @SpringBootApplication
    @EnableEurekaServer
    public class EurekaApplication {
        public static void main(String[] args) throws Exception {
            SpringApplication.run(EurekaApplication.class, args);
        }
    }
    
    • eureka应用已经完成,接下来是服务提供者了

    服务提供者provider

    • -新建名为provider的应用,pom.xml如下,可见是个普通的web工程,会将自己注册到eureka上去:
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>spring-cloud-square-tutorials</artifactId>
            <groupId>com.bolingcavalry</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>provider</artifactId>
        <packaging>jar</packaging>
    
        <dependencies>
            <dependency>
                <groupId>com.bolingcavalry</groupId>
                <artifactId>client</artifactId>
                <version>${project.version}</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-context</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <!-- 如果父工程不是springboot,就要用以下方式使用插件,才能生成正常的jar -->
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <mainClass>com.bolingcavalry.provider.ProviderApplication</mainClass>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>repackage</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </project>
    
    • 配置文件application.yml:
    spring:
      application:
        name: provider
    
    server:
      port: 18080
    
    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8761/eureka/
    
    • 启动类ProviderApplication .java:
    package com.bolingcavalry.provider;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class ProviderApplication {
        public static void main(String[] args) {
            SpringApplication.run(ProviderApplication.class, args);
        }
    }
    
    • web服务类,可见对外提供了两个接口hello-strhello-obj,前者返回字符串,或者返回对象:
    package com.bolingcavalry.provider.controller;
    
    import com.bolingcavalry.client.HelloResponse;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.client.ServiceInstance;
    import org.springframework.cloud.client.discovery.DiscoveryClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.List;
    import java.util.Random;
    
    @RestController
    public class Hello {
    
        public static final String HELLO_PREFIX = "Hello World";
    
        @Autowired
        DiscoveryClient client;
    
        /**
         * 随机取一个provider实例,返回其描述信息,
         * 如果只有一个provider实例时,返回的就是当前服务信息
         * @return
         */
        private String providerDescription() {
            List<ServiceInstance> instances = client.getInstances("provider");
            ServiceInstance selectedInstance = instances
                    .get(new Random().nextInt(instances.size()));
    
            return String.format("serviceId [%s], host [%s], port [%d]",
                    selectedInstance.getServiceId(),
                    selectedInstance.getHost(),
                    selectedInstance.getPort());
        }
    
        private String dateStr(){
            return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
        }
    
        @GetMapping("/hello-str")
        public String helloStr() {
            List<ServiceInstance> instances = client.getInstances("provider");
            ServiceInstance selectedInstance = instances
                    .get(new Random().nextInt(instances.size()));
            return HELLO_PREFIX
                    + " : "
                    + providerDescription()
                    + ", "
                    + dateStr();
        }
        
        @GetMapping("/hello-obj")
        public HelloResponse helloObj(@RequestParam("name") String name) {
            return new HelloResponse(name, dateStr(), providerDescription());
        }
    }
    
    • 这个provider应用算是个最朴实无华的web服务了

    启动服务

    • 现在可以将eureka和provider服务先后启动,这样后面的应用编码完成后可以直接测试

    consumer-okhttp,基于spring-cloud-square的okhttp能力

    • 接下来要创建的应用consumer-okhttp,使用的是spring-cloud-square三种能力的第一种:okhttp
    • pom.xml内容如下,重点是spring-cloud-square-okhttp和spring-cloud-starter-loadbalancer这两个库的引入:
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>spring-cloud-square-tutorials</artifactId>
            <groupId>com.bolingcavalry</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>consumer-okhttp</artifactId>
        <packaging>jar</packaging>
    
        <dependencies>
            <dependency>
                <groupId>com.bolingcavalry</groupId>
                <artifactId>client</artifactId>
                <version>${project.version}</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-webflux</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.squareup.okhttp3</groupId>
                <artifactId>okhttp</artifactId>
                <scope>compile</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-square-okhttp</artifactId>
                <version>0.4.0-SNAPSHOT</version>
                <scope>compile</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-loadbalancer</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <!-- 如果父工程不是springboot,就要用以下方式使用插件,才能生成正常的jar -->
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <mainClass>com.bolingcavalry.ConsumerApplication</mainClass>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>repackage</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </project>
    
    • 配置文件application.yml,还是常见的那几个配置:应用名、端口、eureka:
    spring:
      application:
        name: consumer-okhttp
    
    server:
      port: 18081
    
    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8761/eureka/
    
    • 启动类:
    package com.bolingcavalry.consumer;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class OkhttpApplication {
        public static void main(String[] args) {
            SpringApplication.run(OkhttpApplication.class, args);
        }
    }
    
    • 接下来是重要的配置类OkHttpClientConfig.java,用于实例化OkHttpClient.Builder对象并注册到spring环境:
    package com.bolingcavalry.consumer;
    
    import okhttp3.OkHttpClient;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    class OkHttpClientConfig{
        @Bean
        @LoadBalanced
        public OkHttpClient.Builder okHttpClientBuilder() {
            return new OkHttpClient.Builder();
        }
    }
    
    • 然后就可以使用这个Builder来创建OkHttpClient实例了,如下所示,可见入参request的url字段里使用了服务名provider,相当于OkHttpClient内如也能通过服务名取得具体的服务地址,至于是如何获取的,会在后面的文章详细分析,整段代码除了url使用服务名,并没有什么值得关注的地方了,普通的OkHttpClient使用而已:
    package com.bolingcavalry.consumer.controller;
    
    import okhttp3.OkHttpClient;
    import okhttp3.Request;
    import okhttp3.Response;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import java.io.IOException;
    
    @RestController
    public class RemoteHello {
        @Autowired
        private OkHttpClient.Builder builder;
    
        @GetMapping("/remote-str")
        public String hello() throws IOException {
            // 直接使用服务名
            Request request = new Request.Builder().url("http://provider/hello-str").build();
    
            // 远程访问
            Response response = builder.build().newCall(request).execute();
    
            return "get remote response : " + response.body().string();
        }
    }
    
    • 接下来看看单元测试代码,使用MockMvcRequestBuilders构造http请求,检查返回码和返回内容:
    package com.bolingcavalry.consumer.controller;
    
    import com.bolingcavalry.client.Constants;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.http.MediaType;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
    import static org.hamcrest.Matchers.containsString;
    import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
    
    @SpringBootTest
    @AutoConfigureMockMvc
    @Slf4j
    class RemoteHelloTest {
    
        @Autowired
        private MockMvc mvc;
    
        @Test
        void hello() throws Exception {
            String responseString = mvc.perform(MockMvcRequestBuilders.get("/remote-str").accept(MediaType.APPLICATION_JSON))
                    .andExpect(status().isOk())
                    .andExpect(content().string(containsString(Constants.HELLO_PREFIX)))
                    .andDo(print())
                    .andReturn()
                    .getResponse()
                    .getContentAsString();
    
            log.info("response in junit test :\n" + responseString);
        }
    }
    
    • 如果eureka和provider都运行起来了,那么此时可以直接运行单元测试类,顺利通过测试,如下图:
      在这里插入图片描述

    consumer-retrofit-okhttp,基于spring-cloud-square的okhttp能力

    • 接下来的两个应用都使用了当下热门的retrofit,再搭配Spring Cloud LoadBalance实现服务注册发现,当然了retrofit自身无法完成网络请求处理,要依赖其他库,先看okhttp库的
    • 新建应用consumer-retrofit-okhttp,其pom.xml如下,要注意的必须依赖spring-cloud-square-retrofit和spring-cloud-square-okhttp,另外,为了:
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>spring-cloud-square-tutorials</artifactId>
            <groupId>com.bolingcavalry</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
        <artifactId>consumer-retrofit-okhttp</artifactId>
    
        <dependencies>
            <dependency>
                <groupId>com.bolingcavalry</groupId>
                <artifactId>client</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-square-retrofit</artifactId>
                <version>0.4.0-SNAPSHOT</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-square-okhttp</artifactId>
                <version>0.4.0-SNAPSHOT</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <!--skip deploy (this is just a test module) -->
                    <artifactId>maven-deploy-plugin</artifactId>
                    <configuration>
                        <skip>true</skip>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
    
    • 配置文件:
    spring:
      application:
        name: consumer-retrofit-okhttp
    server:
      port: 18082
    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8761/eureka/
    
    • 启动类:
    package com.bolingcavalry.consumer;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class RetrofitOkhttpApplication {
        public static void main(String[] args) {
            SpringApplication.run(RetrofitOkhttpApplication.class, args);
        }
    }
    
    • 配置类,和前一个应用的没啥区别,想想也是,底层可不都是okhttp么:
    package com.bolingcavalry.consumer;
    
    import okhttp3.OkHttpClient;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.cloud.square.retrofit.EnableRetrofitClients;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @EnableRetrofitClients
    class OkHttpClientConfig{
        @Bean
        @LoadBalanced
        public OkHttpClient.Builder okHttpClientBuilder() {
            return new OkHttpClient.Builder();
        }
    }
    
    • 接下来,有趣的部分出现了,先定义HelloService.java,里面的注解RetrofitClient指定了对应的服务名provider,在hello方法生,用GET注解指定了provider提供的web接口,而且hello方法的返回值Call,和provider服务中hello-obj的返回值HelloResponse也是对应的,还有就是hello的入参对应着provider服务中hello-obj的入参,很熟悉吧,确实,和feign太像了:
    package com.bolingcavalry.consumer.service;
    
    import com.bolingcavalry.client.HelloResponse;
    import org.springframework.cloud.square.retrofit.core.RetrofitClient;
    import retrofit2.Call;
    import retrofit2.http.GET;
    import retrofit2.http.Query;
    
    @RetrofitClient("provider")
    public interface HelloService {
    
        @GET("/hello-obj")
        Call<HelloResponse> hello(@Query("name") String name);
    }
    
    • 接下来是调用provider服务中hello-obj接口的代码RemoteHello.java,如下所示,神奇的一幕出现了,刚才咱们只写了HelloService接口,并没有写它的实现,但是通过Autowired注解却能 从spring环境拿到实例直接使用,在hello方法中,并没有见到远程调用的代码,而是执行helloService.hello,就能发起远程调用,拿到provider返回的结果:
    package com.bolingcavalry.consumer.controller;
    
    import com.bolingcavalry.client.HelloResponse;
    import com.bolingcavalry.consumer.service.HelloService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import java.io.IOException;
    
    @RestController
    public class RemoteHello {
        @Autowired(required = false)
        HelloService helloService;
    
        @GetMapping("/remote-obj")
        public HelloResponse hello(@RequestParam("name") String name) throws IOException {
            return helloService.hello(name).execute().body();
        }
    }
    
    • 看到这里,聪明的您一定会觉得欣宸就是个没见过世面的乡巴佬:定义HelloService 接口,无需开发实现类,这玩意在mybatis不就有了嘛,居然敢说"神奇",我觉得您说得对,欣宸确实没见识,大惊小怪的…
    • 单元测试类如下,由于返回的是json对象,因此可以用andExpect方法再配合MockMvcResultMatchers,对json进行检查:
    package com.bolingcavalry.consumer.controller;
    
    import lombok.extern.slf4j.Slf4j;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.http.MediaType;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    import org.springframework.web.context.WebApplicationContext;
    
    import static org.hamcrest.Matchers.is;
    import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
    
    @SpringBootTest
    @AutoConfigureMockMvc
    @Slf4j
    class RemoteHelloTest {
    
        private MockMvc mvc;
    
        @Autowired
        private WebApplicationContext webApplicationContext;
    
        @BeforeEach
        public void setUp() {
            // 在单元测试的时候,MockHttpServletResponse实例的characterEncoding默认是ISO-8859-1,
            // 得到的字符串打印出来也是乱码,
            // 下面的设置可以解决此问题
            if (null==mvc) {
                mvc = MockMvcBuilders
                        .webAppContextSetup(webApplicationContext)
                        .addFilter((request, response, chain) -> {
                            response.setCharacterEncoding("UTF-8"); // this is crucial
                            chain.doFilter(request, response);
                        }, "/*")
                        .build();
            }
        }
    
        @Test
        void hello() throws Exception {
            // 请求参数是用户名,实时生成一个
            String name = System.currentTimeMillis() + "程序员A";
    
            // 请求
            String responseString = mvc.perform(
                    MockMvcRequestBuilders
                            .get("/remote-obj")
                            .param("name", name)
                            .accept(MediaType.APPLICATION_JSON)
            )
                    .andExpect(status().isOk())                           // 验证状态
                    .andExpect(jsonPath("$.name", is(name)))    // 验证json中返回的字段是否含有name
                    .andDo(print())
                    .andReturn()
                    .getResponse()
                    .getContentAsString();
    
            log.info("response in junit test :\n" + responseString);
        }
    }
    
    • 执行单元测试,如下图,顺利通过:
      在这里插入图片描述

    consumer-retrofit-webflux,基于spring-cloud-square的retrofit + webflux

    • 最后登场的是consumer-retrofit-webflux,pom.xml如下,依赖库是spring-cloud-square-retrofit + spring-boot-starter-webflux的组合:
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>spring-cloud-square-tutorials</artifactId>
            <groupId>com.bolingcavalry</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>consumer-retrofit-webflux</artifactId>
    
        <dependencies>
            <dependency>
                <groupId>com.bolingcavalry</groupId>
                <artifactId>client</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-square-retrofit</artifactId>
                <version>0.4.0-SNAPSHOT</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-square-retrofit-webclient</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-webflux</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <!--skip deploy (this is just a test module) -->
                    <artifactId>maven-deploy-plugin</artifactId>
                    <configuration>
                        <skip>true</skip>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
    
    • 配置文件application.yml:
    spring:
      application:
        name: consumer-retrofit-webflux
    
    server:
      port: 18083
    
    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8761/eureka/
    
    • 启动类RetrofitWebfluxApplication.java
    package com.bolingcavalry.consumer;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class RetrofitWebfluxApplication {
        public static void main(String[] args) {
            SpringApplication.run(RetrofitWebfluxApplication.class, args);
        }
    }
    
    • 配置类AppConfiguration.java,使用的注解是EnableRetrofitClients,实例化的Buider对象是WebClient.Builder,和前面的不一样,要格外注意:
    package com.bolingcavalry.consumer;
    
    import okhttp3.OkHttpClient;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.cloud.square.retrofit.webclient.EnableRetrofitClients;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.reactive.function.client.WebClient;
    
    @Configuration
    @EnableRetrofitClients
    class AppConfiguration {
        @Bean
        @LoadBalanced
        public WebClient.Builder builder() {
            return WebClient.builder();
        }
    }
    
    • 接下来是接口定义,注意hello方法的返回值是Mono,这是weflux风格的返回值,代表异步的0个或一个元素:
    package com.bolingcavalry.consumer.service;
    
    import com.bolingcavalry.client.HelloResponse;
    import org.springframework.cloud.square.retrofit.core.RetrofitClient;
    import reactor.core.publisher.Mono;
    import retrofit2.http.GET;
    import retrofit2.http.Query;
    
    @RetrofitClient("provider")
    public interface HelloService {
    
        @GET("/hello-obj")
        Mono<HelloResponse> hello(@Query("name") String name);
    }
    
    • 最后是单元测试类,和前面的没啥区别:
    package com.bolingcavalry.consumer.controller;
    
    import lombok.extern.slf4j.Slf4j;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.http.MediaType;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    import org.springframework.web.context.WebApplicationContext;
    import static org.hamcrest.Matchers.is;
    import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
    
    @SpringBootTest
    @AutoConfigureMockMvc
    @Slf4j
    class RemoteHelloTest {
    
        private MockMvc mvc;
    
        @Autowired
        private WebApplicationContext webApplicationContext;
    
        @BeforeEach
        public void setUp() {
            // 在单元测试的时候,MockHttpServletResponse实例的characterEncoding默认是ISO-8859-1,
            // 得到的字符串打印出来也是乱码,
            // 下面的设置可以解决此问题
            if (null==mvc) {
                mvc = MockMvcBuilders
                        .webAppContextSetup(webApplicationContext)
                        .addFilter((request, response, chain) -> {
                            response.setCharacterEncoding("UTF-8"); // this is crucial
                            chain.doFilter(request, response);
                        }, "/*")
                        .build();
            }
        }
    
        @Test
        void hello() throws Exception {
            // 请求参数是用户名,实时生成一个
            String name = System.currentTimeMillis() + "程序员B";
    
            // 请求
            String responseString = mvc.perform(
                    MockMvcRequestBuilders
                            .get("/remote-obj")
                            .param("name", name)
                            .accept(MediaType.APPLICATION_JSON)
            )
                    .andExpect(status().isOk())                           // 验证状态
                    .andExpect(jsonPath("$.name", is(name)))    // 验证json中返回的字段是否含有name
                    .andDo(print())
                    .andReturn()
                    .getResponse()
                    .getContentAsString();
    
            log.info("response in junit test :\n" + responseString);
        }
    }
    
    • 运行单元测试,如下图,顺利通过,并且红框中所示的中文也没有乱码:
      在这里插入图片描述
    • 至此,spring-cloud-square的三种类型,咱们全部编码体验了一遍,聪明的您当然不会只满足于使用它们,接下来文章,咱们就去深入spring-cloud-square源码,研究其实现的细节,欣宸原创,必不会辜负您的期待!

    你不孤单,欣宸原创一路相伴

    1. Java系列
    2. Spring系列
    3. Docker系列
    4. kubernetes系列
    5. 数据库+中间件系列
    6. DevOps系列
    展开全文
  • 3、项目经历 在我看来,项目经历是一锦上添花的内容主要是给面试官一提问的切入点。 项目/框架等工程能力,主要是针对工作3~5年的社招人员进行考核,因为他们理应有解决实际问题的经验。但是作为校招生,...

    点上方计算机视觉联盟获取更多干货

    仅作学术分享,不代表本公众号立场,侵权联系删除

    转载于:作者丨ELon Z,链接:https://imageslr.com/2021/autumn-recruit.html

    来源丨AI机器学习与知识图谱

    编辑丨极市平台

    AI博士笔记系列推荐

    周志华《机器学习》手推笔记正式开源!可打印版本附pdf下载链接

    目录

    • 1、前言

    • 2、时间线

    • 3、考察维度

    • 基础知识

    • 算法能力

    • 项目经历

    • 实习经历

    • 工程能力

    • 4、开始复习

    • 如何做笔记?

    • 基础知识复习指南

    • 算法复习指南

    • 5、准备简历

    • 6、开始面试

    • 面试前

    • 面试前一小时

    • 面试中

    • 面试后

    • 7、不同公司的面试流程对比

    • 8、收获 offer

    • 9、LeetCode 题解

    • 10、社区

    • 11、综合面试问题

    • 12、Q&A

    一、前言

    最近博客的阅读人数逐渐增加。回想起去年这个时候,我正在为校招做准备,很多文章也都是当时整理的。估计现在的读者很多都在为实习或校招做准备,所以趁现在有时间,分享一下自己的经验教训,希望能对大家有所帮助。

    我的背景是 211本、985计算机硕,两段实习经历、一个项目/竞赛经历。我的校招面试从 2020 年 7 月初开始,到 9 月初结束,期间共面了 10+ 家互联网公司 (BAT/TMD/Y/K/P/MS 等),全部都是后端开发岗位,技术栈不匹配挂了 2 家 (DD、JD)。面试一圈下来,感觉不同公司的考察点都大同小异,评价标准也比较相似。如果能提前有一个大致认知,复习时兴许能事半功倍,少走弯路。

    二、时间线

    校招一般分为提前批、秋招、秋招补招、春招等阶段。

    提前批一般在 7 月开始,但是 2020 年受到疫情影响,各家大厂早在 6 月就开始了提前批,比如字节提前批是 6.16~7.16、阿里是 6月中旬开始。今年预估还是 6 月开始。

    我参加了提前批,所以是在 6 月下旬开始报名 + 笔试,7 月下旬集中面试(每天一两场面试),8月接近尾声(主要是三面 or HR 面),9 月上旬结束。也有同学不参加提前批,是在 8 月上旬开始,9 月集中面试,9 月下旬结束。

    在这里,我的经验是:要尽可能早地准备、参与面试,有机会尽量参加提前批。

    参加提前批意味着多一次面试机会,多投几家就能积累更多的经验,无论通过与否,对于之后的面试都有帮助。提前批也不是大佬专场,入场率和通过率都比想的要高。而且提前批大部分都免笔试,所以没什么试错成本。

    提前批还可以集中面试很多家。校招面试就好比高三模考,天天面试就会习惯面试,好处一是不再紧张,二是能够维持面试的手感,三是能在面试中暴露自己更多的问题,及时查漏补缺。

    以往的说法是金九银十,但我感觉金八银九或许更准确些。去年身边的大部分同学在 9 月就已经拿到了心仪的 offer。当其他人手上至少一个保底 offer 而你还没有开始面试时,心里还是会比较焦虑的。所以还是要早做准备。

    三、考察维度

    各个公司对实习生的考察点大致可以分为以下几个维度:基础知识、算法、项目经历、实习经历、工程能力。我认为前两者更为重要。

    1、基础知识

    对于实习生/校招生来说,基础知识是最重要的考察点。

    基础知识就是操作系统、计算机网络、数据库、计算机组成原理、编译原理等课程的内容 (重要性递减)。作为实习生/校招生,大部分人都没有出彩的项目或实习经历,但都在学校系统学习了基础课程。因此,在很多面试官看来,基础知识最能反映校招候选人的水平。一方面,对基础知识的掌握程度体现了候选人的学习态度和接受能力;另一方面,实际工作也需要候选人有扎实的计算机基础。

    2、算法能力

    算法能力同样很重要。特别是对于后端同学来说,每轮面试至少 2 道算法已经成为标配,某些外企(如微软)甚至只考算法题。

    面试官看重算法能力的原因:一方面,只要肯付出时间和精力去刷题,就可以达到一个不错的水平,所以算法能力至少应当达到平均线;另一方面,算法题需要深入积累、举一反三,才能解决面试中出现的新的题目,这反映了候选人的学习和思维能力。

    算法题刷到什么程度就可以?其实很多面试官出的题目都是 LeetCode mid 难度,所以算法能力达到 LeetCode mid 以上就可以。刷完 LeetCode 前 300 道的 mid 题和一些高频/经典 hard 题,就可以应对大多数的面试了。

    不过,相比于算法能力,我认为基础知识更为重要一些。算法是一项长期工作,见效慢,需要持续投入;而基础知识可以先广度覆盖各类知识点,再深度研究每个专题,在学习过程中可以随时获得反馈。算法的评判标准较为客观绝对,要么做出来要么做不出来;而基础知识的考察相对来说要宽松一些,不管会还是不会都可以说上一两句,面试过程中有更多的机会展示自己。当然,这也取决于面试官的喜好,不同团队/方向的考察侧重点也不同,所以基础知识和算法还是要两手抓。

    3、项目经历

    在我看来,项目经历是一个锦上添花的内容,主要是给面试官一个提问的切入点。

    项目/框架等工程能力,主要是针对工作3~5年的社招人员进行考核,因为他们理应有解决实际问题的经验。但是作为校招生,我们很难有大型项目的经历,面试官也不知道我们写过的项目的质量如何,或者不知道我们说的是真的还是在吹牛,所以会更关注我们的基础(如操作系统、数据库)与学习能力(如算法)。因此,不需要在项目经历上花费很长时间准备——性价比不高,也不需要将项目经历包装得高大上——面试官不信。

    但是简历上还是要有一段项目经历,以给面试官一个不同的提问角度。

    面试官对项目经历的提问,一般会从“介绍一下你做了什么”开始,然后会问一些业务细节,比如“为什么选这个方案?”/“有没有遇到xx问题?如何解决的?”如果我们的项目经历比较常规,大部分问题就会围绕里面用到的技术展开,而这些又最终会落到基础知识上。

    面试官会注意候选人在项目过程中用了哪些技术、哪些框架,然后深入地问原理。面试官的关注点并不是“我在项目经历中,使用了哪些技术”,而是“我在项目经历中,使用了这些技术,并且我对其原理有深入的了解”。在项目中使用工程上很流行的技术和框架,这是最基本的水平;如果对这些框架的原理有深入的了解、对比过不同框架的优缺点、甚至自己尝试实现过一个 demo 框架,那在面试官那里一定是一个极大的加分项。

    我们应当保证对项目中用到的技术/框架有较为深入的了解,而不只是停留在调用 API 上。比如项目里用了 gin 框架,面试官就会问为什么 gin 的速度快?有没有从源码的层面上理解过它的特点?这个时候,如果我们知道是前缀树,面试官就会点点头;如果我们再顺手写个前缀树的代码,那就会让面试官惊喜了。

    一个常见的误区是:项目经历一定要高大上。比如,要不要做一个秒杀系统然后写在简历上?我认为没有太大必要,因为校招生很少有在大型分布式环境下编写 redis、消息队列等代码的条件,那这种项目经历不就是在 github 上照猫画虎吗?

    项目不是只能做 Web 网站或者后台管理系统,还可以有其他选择。比如:

    学习《CSAPP》课程,并做完若干 Labs,如内存缓存、内存分配回收、Web 服务器等 实现一个简单的操作系统,可以基于已经写好的系统,实现其中的部分模块,如进程调度、中断处理等 实现一个编译器,或者正则表达式(子集)的解析器 实现一个基于 TCP 的并发服务器框架,包括路由、Goroutine 池、消息队列等(如 zinx) 实现一个简单的 RPC 框架,考虑选择什么传输协议和编码协议、以及这样选择的原因 build-your-own-x 提供了许多实践项目,如 Docker、数据库、命令行工具、Shell、Git、搜索引擎等 这些项目不仅可以解决实际问题,还可以在过程中深入钻研底层原理,或者锻炼自己的算法能力。比起堆积木、搭脚手架,我认为这些项目更值得尝试。

    4、实习经历

    实习经历可以看作某种形式的项目经历,能够丰富简历、作为面试考点,大厂的实习经历也是对我们能力的背书。

    面试官对实习经历的关注点:

    在实习中做了什么工作?遇到哪些问题?如何解决?通过实习,得到了哪些成长?比如 Git 操作、内部工具、团队协作、开发流程/规范等。实习过程有哪些产出?有没有主动的贡献?比如整理新人手册、发现并解决了某个问题、提升了业务指标等。对团队的现状和下一步规划,有没有自己的思考?在秋招(7、8月)之前,最好能够有至少一个实习经历。如果复习时间紧张,可以先投一些竞争较小的岗位,比如大厂的非核心部门、新业务/缺人的部门,或者二线互联网公司/独角兽企业等。

    去哪儿找实习信息?首先是各个公司的官网,其次是牛客网(求职-实习广场)和实习僧,最后还有来自同学的内推。不要担心内推会麻烦别人,大部分正式员工都有招新同学的 KPI,我们只需要做一份简历,然后交给他们就好。

    一个公司可以投递多个不同部门的岗位,但同一时刻只能进入一个岗位的面试流程。不同部门对候选人的要求不同,所以面试没有通过不一定代表水平不够,可以尝试投递同一个公司的其他部门。

    因为培养实习生需要成本,所以部门希望实习生能够实习较长时间,一般都要 6 个月以上,至少 3 个月,但如果面试过程中说只能实习 3 个月很有可能被刷掉。

    暑期实习有大概率转正。转正可以有一个保底 offer,面试其他公司可以拿来 argue,但是转正的评级一般不会太高 [如何提高?]。即使没有转正名额,也可以通过校招通道来争取更高级别的 offer。

    5、工程能力

    工程能力主要考察:对开发环境 / 工具的熟悉程度,如 Linux 概念、Linux 命令、 Git 操作等;服务运维经验,如问题排查、性能优化等。面试题汇总中总结了部分常见题目。这里需要自己去搜集和动手操作。

    四、开始复习

    现在开始复习(3~5月),时间较为充裕。我们应当扎实掌握每一个知识点,而不是以覆盖更多面试题为目标。重质不重量,重深度不重广度,逐步构建个人知识体系。

    现在网上有许多的面经汇总,包含一系列的题目和简短的回答。光靠这些面经的答案是远远不够的。面试官很喜欢在一道题上持续深入的追问,这很容易把浅尝辄止的候选人筛出去。我们应该将这些面经汇总看作问题集,每一个问题的答案,需要自己去深入挖掘。

    许多面试题很常见,网上有详细的文章,但只会背八股文也远远不够。理解不牢,换个问法可能就不会了,背八股文也容易被面试官发现,或者被一连串的追问卡住。所以深入思考、主动归纳总结更为重要。

    这里我的经验是做笔记。一份笔记,在写的过程中需要整理、思考和归纳,可以加深对知识点的理解,在复习过程中也是很好的参考资料。一系列笔记就是自己的知识库。

    1、如何做笔记?

    推荐使用 Markdown 做笔记。Markdown 的语法简单,书写过程中无需设置样式,不会打断思路。Markdown 也有较好的扩展性,可以和 HTML 语法结合使用。个人认为,Markdown 是最适合程序员的笔记格式。我的博客就是用 Markdown 写的。

    Mac 系统下,推荐使用 MWeb 管理笔记。可选的替代方案有思源笔记、Typora、VS Code 等。MWeb 界面截图:

    做笔记的过程中,应当遵循以下几个基本原则:

    笔记不是简单地复制粘贴,而是要主动梳理,深入思考。笔记不是速记,即使不公开给他人,也要行文流畅规范。遣词造句的过程,实际是在脑海中梳理知识的过程。多问几个为什么,多发掘可能的考点。没有现成答案的问题,需要自己去探索和归纳。笔记的分类:

    基础课程:操作系统、计算机网络、数据库等 算法:解题技巧、专题、题解等 语言:C/C++、Golang、Java 等 工具:Git、Linux、Docker 等 其他:分布式、设计模式、最佳实践等 Tips:

    可以给笔记打标签,方便查找 可以在笔记的开头或结尾罗列参考文献 MWeb 可以通过 CMD + O 快捷键或 Alfred Workflow 搜索文档

    2、基础知识复习指南

    最重要的是操作系统、计算机网络,其次是数据库等。我的复习方法:

    初步建立知识框架:选择一本经典本科教材,系统过一遍。本科教材知识点全,容易理解,但缺乏深度。深入研究各个专题:深入研究面经中列出的每个题目,特别是高频题目,如进程线程、HTTP 等。面经可以在很多渠道找到,比如牛客网、Github 上的面试题汇总。这里一定要自己整理笔记,我采用三个步骤:先搜集大量参考文章;阅读各个文章,同时梳理笔记大纲,粘贴一些可参考的关键段落;完善笔记内容,整理为一篇系统、连贯的文章。穿插阅读经典书籍:网上搜到的笔记大多是二手知识,直接阅读权威书籍,会有更原汁原味的体验。书籍有完整的上下文,随时可以向前或向后翻阅。时间有限的情况下,可以不从头到尾阅读,而是只阅读某个专题。可以去豆瓣或知乎查找高分/推荐图书,如《深入理解计算机系统》《现代操作系统》《Operating System: Three Easy Pieces》等。分清优先级:以基础知识、高频考点为主。像是分布式、中间件等进阶内容,应该是在有余力的情况下再深入了解。这些内容看起来高大上,但没有对应业务场景的情况下,只能是背八股文,一问就露馅。Tips:

    不懂的名词多查查,多问几个为什么,发散思维,扩展相关知识点 网上的内容不一定正确,需要自己辨别或验证 网上的资料比较琐碎,自己整理一下,印象更为深刻 使用 Google 搜索;屏蔽内容质量低、抄来抄去的网站(比如 C**N)

    可以尝试搜索英文关键字,内容质量较高

    3、算法复习指南

    算法能力达到 LeetCode mid 以上即可应对大部分面试。

    我的个人情况:《剑指offer》50+ 道、LeetCode 242 道 (73 easy + 137 mid + 32 hard,范围主要是前 300 题 + 热题 HOT 100 + 腾讯精选练习 50),题目数量不算多。

    我的刷题方法:

    持续学习:前期刷一些 easy 题热身,之后每天 1~2 道 mid 或 hard。定期复习:使用一个表格记录自己的错题、错误原因、笔记链接、上次复习时间,每周复习 3~5 道。按照标签刷:按照链表、数组、二叉树、动态规划等标签刷题,同一个标签的代码和方法类似,效率高。分清优先级:优先刷《剑指offer》、LeetCode 经典题、高频题、模板题;冷门题就没必要浪费时间了。可以参照网上的 LeetCode 笔记的顺序来刷题,见附录 - LeetCode 笔记。吃透每道题:刷题的目标不只是通过,而是要给出最优解。对于一题多解的情况,还需要参考他人的题解,将各种解法都实现一遍,并对比不同解法的优缺点。此外,很多不同的题目有着相似的解题思路,可以尝试用这道题的解法去做其他题,争取举一反三,比如 42. 接雨水和 84. 柱状图中的最大矩形。最后,总结自己的笔记。这样做下来,虽然刷题数量不多,但每道题都很会掌握得很扎实。Tips:学习数据结构时,配合可视化网站有奇效。推荐:

    Data Structure Visualizations visualgo Algorithm Visualizer 关于“定期复习”,我使用一个表格来管理所有错题:

    按照标签分组;相似题目可以汇总到一起,比如上图的“打家劫舍”。通过颜色区分题目的重要程度:橙色表示重点题目,优先复习;黑色表示常规错题,需要复习;灰色表示不重要的题目,有些细节需要注意,可以不复习;删除线表示已掌握的题目,不需要复习。每一行备注中的超链接是 MWeb 中的笔记链接,点击可跳转到笔记;备注里也可以记录需要注意的细节和易错点。软件是 OmniOutliner,使用 Excel 或飞书云文档-多维表格可以实现同样的效果。首次复习时都需要重新写一遍代码,之后视情况选择写代码或者只回忆思路。及时更新各个题目的状态为“重点”“不重要”或“已掌握”,防止错题越攒越多。面试前快速浏览表格,回忆做题思路,优先看橙色高亮的重点题。

    五、准备简历

    简历从上到下大致为:个人信息、教育背景、实习经历、项目经历、荣誉奖励、专业技能/英文水平/个人评价等。我的简历模板:

    上图使用 Sketch 绘制。制作一份这样的简历很简单:首先确定一个主题色,如 #353535, #3F59A8, #4F6F8A, #0089B2, #870509 等;其次保证行高、行间距、页边距都是 4 的倍数,如上图页边距为 24px、行间距为 4px / 8px/ 12px;矢量图标可以从 iconfont 查找。

    简历可以使用在线平台制作,如乔布简历、超级简历 CV 等,也可以使用 Word / PS / Sketch 制作。拒绝花花绿绿、华而不实的风格,关注内容而不是样式。采用 STAR 法则描述自己的项目和实习经历,知乎的这个问题可以参考,geekcompany/ResumeSample 提供了一系列程序员的简历模板,resumejob/awesome-resume 则提供了许多程序员的简历例句。

    Tips:

    最好放在一页上,如果一页放不下,可以缩小字体/间距,或改变页面大小;部分在线平台也提供“自动填满一页”的布局功能。简历导出为 PDF 格式,命名为“姓名_学校_岗位_手机号”。教育经历写预计毕业时间,而不是“至今”。

    六、开始面试

    1、面试前

    准备一份个人介绍 个人介绍相当于简历的文字形式。大部分面试官在开始面试时才会查看我们的简历,所以个人介绍其实是引导面试官读简历的过程,最好能和简历从上到下的内容对应。

    如果面试外企,还需要准备英文简历和英文个人介绍。

    个人介绍大致可以分为三个部分:

    背景介绍(学校、专业、荣誉奖励等) 实习经历、项目经历(遵循 STAR 法则) 业余生活(博客、Github 开源经历、个人提升等) 个人介绍模板:

    (背景介绍) 面试官您好,我叫 XXX。来自 XX 大学 XX 学院,目前研 X 在读,明年 6 月毕业,研究方向是 XXX。本科我就读于 XX 大学,XX 专业。在校期间,我多次获得国家级、校级奖学金。此外,我也参加过许多软件开发竞赛,包括 XX 大赛、XX 大赛等,拿到了 XX 奖项。

    (实习经历) 去年,我在 XX 公司的 XX 团队实习 X 个月。这个部门主要是做 XXX (介绍团队业务背景),我在其中负责 XXX 工作,包括 XXX、XXX 等。此外,我还输出了 XX 篇文章,整理为部门新人手册。

    (业余生活) 最后,我业余时间有写博客的习惯,会定期归纳总结自己的笔记。我也有开源经历,在 github 上有几个开源项目。

    以上就是我的个人介绍,谢谢您。

    Tips:

    每个部分之间要有连接词。注意语速、节奏和停顿,避免变成高中生背课文;可以自己提前录音排练。时长控制在三分钟以内。一些经常被问到的问题,可以补充到自我介绍里。比如面试官总会问我:“你为什么从前端转到后端?”我就补充到了自我介绍的两个工作经历之间。准备一些常见问题的回答 简历里的项目经历、实习经历最有可能被面试官作为第一个问题,比如“从现在看来,你觉得这个工作还可以怎么改进?”“你觉得这些工作中最难的一点是什么?你是怎么解决的?”针对这些问题,应该提前准备。

    附录 - 综合面试问题中总结了我在技术面和 HR 面遇到的综合面试问题,大家可以结合自己的实际经历,提前准备草稿。

    准备反问面试官的话 面试结束后,面试官会说:“你还有什么要问我的吗?”这时借机可以了解一下团队。选工作是一个双向的过程,了解团队可以帮助做出选择。

    反问技术面试官的问题:

    组内主要的技术栈 / 语言?部门具体业务 / 面试官的工作内容?部门业务的挑战 / 难点?我加入部门后可能负责的工作内容(判断是否感兴趣) 部门的人数 / 人员构成(判断是否核心) 部门业务的后续规划(判断公司投入力度 / 未来机会) 针对新员工有哪些培训(万能问题) 面试官的工作体验,比如工作是否枯燥乏味、对个人成长的帮助、工作压力是否很大、加班强度等。后续是什么流程?还有几轮面试?(推断自己这一面是否通过) 大概多久能够出结果?(避免一直等结果心慌) 您对我刚才面试表现的评价?您对我有哪些建议?/ 哪些方面的知识、技能还可以再提升?/ 您觉得我还有哪些方面的不足?部门所在工区 / 地理位置(考虑通勤成本) 上下班时间 / 是否需要打卡?反问 HR 的问题:

    针对新员工有哪些培训?对我有哪些评价或者建议?大概多久能够出结果(避免一直等结果心慌) 之前的面评(一般 HR 不会透露) 公司福利 / 三餐 / 房补 注意:HR 面试时,不要直接问户口、薪资等问题,等收到 Offer 后、在沟通薪资时再问。

    实习生还需要关注这些问题:

    部门有没有转正机会?实习多长时间之后可以申请转正?对实习出勤的要求是一周至少几天,至少实习多久?部门的转正名额 / 转正概率?如果我想转正,您期望我达到什么水平 / 有多少产出?过去是签外包团队还是正式实习生?不同身份的转正机会如何?其他资源:

    yifeikong/reverse-interview-zh - 反向面试 当面试官问“你有什么要问我的吗”时,应该说什么?安装录屏软件 如果是远程面试,推荐打开录屏 / 录音功能,录制面试过程。

    为什么要录屏 / 录音?面试过程中,我发现不同公司 / 不同轮次的面试题目有很大概率重复,这可能是因为面试官会根据候选人的经历、水平,针对性地挑选问题,我们的回答也可能影响面试官的下一个问题。因此,面试后的复盘非常重要。优先复习自己面试中遇到的题,其次再看网上面经的题,保证同一个题目只会答错一次。此外,也可以通过录屏 / 录音以第三人称的视角观察自己的面试表现,从而发现不足并及时改进。

    Mac 系统默认只能录制麦克风的输入,需要配置 SoundFlower 来录制系统声音 (即面试官的声音)。Mac 系统的录屏软件可以使用 Kap。

    我使用 2015 年 MBP + 关闭所有无关软件 + 打开录屏功能 + 面试一小时,系统没有出现明显卡顿。最好自己提前测试一下。

    2、面试前一小时

    快速浏览以下内容,遇到不会的不用深究,主要是有个思路:基础知识题目列表 综合面试问题 算法错题集,优先重点题目 牛客网面经 调试设备,包括网络、摄像头、耳机、麦克风、录屏功能等;视频画面里不要有杂物。面试前五分钟,排练一遍自我介绍。

    3、面试中

    如果遇到不会的题目怎么办?

    即使不会做,也要主动和面试官交流。先说你如何考虑这道题目,你现在的解题思路是什么,然后向面试官要引导 (比较晦涩) 或提示 (比较直接),最后给出一个自己水平内的最优解。

    4、面试后

    复盘。根据录屏或者回忆,整理面试中遇到的题目、自己没有发挥好的地方。距离面试结束的时间越短,回忆越清晰,可以尽快整理。

    查漏补缺。有些题目可能只是我们自认为答对了,所以对于每道题目,都需要搜集资料、发掘所有的考点、并做笔记。

    七、不同公司的面试流程对比

    按照字母顺序排列;仅代表个人经历,不保证今年依然适用。技术面重点考察基础知识 / 算法;经理面以综合面试问题为主;交叉面是其他部门的人来面试。

    1、阿里 面试难度:☆☆☆☆☆

    面试流程:统一在线笔试。2 轮技术面 + 1 轮经理面 + 1 轮交叉面 + 1 轮 HR 面,每轮都是电话面试,时长 1 小时左右。一面面试官是未来的 leader,之后的面试官应该级别更高。一二面考察基础知识,三四面主要围绕项目和实习经历展开。

    反馈周期:通过后会很快约下一轮面试,可以向你的内推人 / 师兄咨询面试结果;每轮面试间隔 2 天~1 周;Offer 统一在 9 月发出。

    评价:由于是电话面试 + 已经笔试过,所以面试时不再考察算法。这导致面试题的覆盖面广、题量大、问得细,难度总体较高。HR 面体验很好,给我提了中肯的建议。

    2、百度 面试难度:☆☆☆☆

    面试流程:3 轮技术面 + 1 轮交叉面 (可能没有) + 1 轮 HR 面。技术面时长 1 小时,HR 面很短。是否有交叉面取决于部门和面试评级。技术面试包括算法题,中等偏难。使用百度的“如流”面试。

    反馈周期:一面完可能紧接着就是二面。1~2 天约下一轮面试。HR 会加微信。

    评价:不同部门的面试官水平差异极大。如果一个月还没给你一个明确的答复,大概率是被泡池子了,可以考虑终止流程,重新投递另一个部门。

    3、快手 面试难度:☆☆☆☆

    面试流程:2 轮技术面 + 1 轮经理面 + 1 轮 HR 面。技术面每轮都是牛客网面试,时长 1 小时。HR 面时长 30 分钟。

    反馈周期:1 周左右约下一轮面试。

    评价:基础架构部门问了很多 C/C++ 的问题,很少问上层的网络协议等知识,也没怎么考算法。面试官比较 nice,答错了会一步一步引导。

    4、美团 面试难度:☆☆☆

    面试流程:2~3 轮技术面,1 轮 HR 面。技术面时长 1 小时,HR 面时长 30 分钟。最后一轮技术面是 leader。使用牛客网面试。

    反馈周期:3 天约下一轮面试。

    评价:面试轮数和面试评级没有关联,2 轮也可以拿 SP。

    5、拼多多 面试难度:☆☆☆☆

    面试流程:2~3 轮技术面 + 1 轮 HR 面。技术面时长 1 小时左右,HR 面时长 30 分钟。技术面会考察两道算法题,都是 LeetCode 高频题,难度适中。使用自研平台面试,类似于牛客网,有代码编辑器和视频窗口。

    反馈周期:一周左右约下一轮面试。

    评价:确实很拼,约了 21:30 面试,面试官有事,等到 22:30 才开始,面完已经 23:30 了。从视频窗口能看出工位确实不大。

    6、腾讯 面试难度:☆☆☆☆

    面试流程:2 轮技术面 + 1 轮经理面 + 1 轮 HR 面。技术面时长 1 小时左右,HR 面时长 30 分钟。三面是 leader。面试形式有电话、牛客网、腾讯会议、QQ 视频。

    反馈周期:2~3 天出面试结果 + 约下一轮面试。如果 5 天还没有约下一轮面试,建议联系 HR 查看进度。join.qq.com 上的进度超过 7 个工作日(一般 10 个自然日)没有更新时,流程会自动终止,简历重新放回池子里。面试通过后会先收到“云证”邮件。

    评价:腾讯在面试实习生和校招生时,比较重视基础课程(操作系统、计算机网络),不会涉及太多高深技术,面试题目和面经重合度较高。每轮都有两三道算法,难度适中,不需要特殊技巧。

    WXG:2 轮技术面 + 2 轮面委会 + 1 轮 HR 面。难度比其他部门高,会考察系统设计题(如高并发定时器)或复杂算法题(如判断点是否在封闭图形内)。

    7、小米 面试难度:☆☆☆

    面试流程:2 轮技术面,时长 1 小时。没有 HR 面。使用牛客网面试。

    反馈周期:3 天约下一轮面试。

    评价:面试官很耐心,会主动指出没答出的题目是哪个知识点、下来以后可以查一下。基本没有能够联系到 HR / 面试官的方法。感觉招人比较随意,二面完一个月没消息,一直认为自己挂了,结果 9 月底突然打电话,直接给出了薪资方案。

    8、猿辅导 面试难度:☆☆☆☆

    面试流程:先笔试,通过后进入面试。2 轮技术面 + 1 轮经理面,每轮时长 45 分钟,其中 15 分钟过简历 + 考察基础知识,25 分钟做两道中等难度的算法题,剩下 5 分钟反问。三面是 leader,没有 HR 面。使用牛客网面试。

    反馈周期:一周内约下一轮面试。如果进入下一个阶段,内推人会先收到通知,可以问内推人。

    评价:整体体验不错,HR 对校招生很用心,会加微信、拉微信群、送校招礼物。猿辅导笔试和面试的算法难度是 LeetCode 前 300(我也只刷了这些题)。基础知识 / 算法 / 实习经历至少有一项突出,会比较稳。

    9、字节跳动 面试难度:☆☆☆☆☆

    面试流程:2 轮技术面 + 1 轮经理面 + 1 轮 HR 面。技术面时长 1 小时,包括 2 道中等难度偏上的算法题。HR 面时间较短。日常实习可能没有经理面,校招可能会有加面。

    反馈周期:一二面一般会连着,三面隔一两天。

    评价:对算法和基础知识都很看重,问得比较细。无论通过与否,反馈都很快。会有 HR 加微信,一般是实习生。

    八、收获 offer

    如何选择 offer?

    TODO 如何拒绝 offer?拒 Offer!- 内推熊

    如何和 HR 谈条件?TODO

    九、LeetCode 题解

    1、labuladong/fucking-algorithm:强烈推荐,非常赞的算法题解。

    2、afatcoder/LeetcodeTop:各个大厂近期的高频面试题汇总。

    3、MisterBooo/LeetCodeAnimation:可视化地呈现 LeetCode 题解。

    4、yuanguangxin/LeetCode

    5、azl397985856/leetcode

    6、youngyangyang04/leetcode-master

    7、Jack-Cherish/LeetCode

    8、Github - Search · LeetCode

    十、社区

    1、牛客网:面经分享、笔试交流、Offer 选择

    2、脉脉:部门评价、职场交流、Offer 选择 OfferShow

    3、小程序:薪资分享

    十一、综合面试问题

    如果需要某个题目的答案模板,请评论指出。

    1、实习期间的工作内容,介绍一下?

    2、简单介绍一下你这个项目的内容?

    3、你觉得它的亮点 / 难点有哪些?

    4、实习期间遇到最大的挑战是什么?如何解决?

    5、实习期间给你带来最大成长的工作是什么?

    6、从现在看来,你觉得这个工作还可以怎么改进?

    7、你觉得这些工作中最难的一点是什么?你是怎么解决的?

    8、你觉得实习期间相比于实习前,对你的最大提升是什么?

    9、有哪些印象深刻的事故 fix?

    10、实习期间有哪些工作以外的对团队的贡献?

    11、之前三轮面试感觉如何?

    12、有在面别的公司吗?有收到 offer 吗?正在哪里实习?转正通过没有?

    可以说正在面试流程中,但尽量不要说已经进 HR 面、或者已经收到 offer 了

    13、你如何选择不同 offer?/ 已经想好去哪个公司了吗?/ 有倾向吗?、

    14、你是不是投过别的公司?如果都要你怎么办?

    15、自己平时分析过源码吗?

    16、你是怎么学习新技术的?

    17、最近在学哪些新技术 / 在看哪些书?

    18、你的长期职业规划是什么?1~3 年的规划是什么?

    19、你能实习多长时间?你实习什么时候可以开始?

    20、你们老师平时项目多吗?管的严吗?

    21、你是什么专业?为什么不做算法而做开发?你如何看待很多本科生毕业后就去找算法岗?

    22、为什么想要读研?

    23、大量工作、有限时间怎么办?

    24、你是如何团队合作的?

    25、为什么想来我们公司?

    26、为什么要应聘这个职位?

    27、你哪里符合这个岗位的需求?

    28、对这个职位,你还欠缺什么?

    29、你对工作有什么要求?

    30、你希望与怎样的领导共事?

    31、如果我录用你,你将怎样开展工作?

    32、如果我录用你,你觉得是为什么?

    33、如果我没有录用你,你觉得是为什么?

    34、如果你在这次面试中没有被录用,你怎么打算?

    35、兴趣爱好是什么?你的优点是什么?你的缺点是什么?

    36、和其他同学相比,你觉得你的优势在哪里?

    37、你认为其他同学如何评价你?/ 你觉得周围人眼中的你是什么样的?

    38、如果我问他们会得到一个什么样的评价?

    39、你最有成就感的事是什么?

    40、生活 or 实习?你遇到的最大的挫折是什么 / 最遗憾的事?

    41、大学期间,最大的收获是什么?

    42、有哪些帮助别人的经历?

    43、你实习、生活中遇到的最难忘的人是谁?TA 有哪些特质?

    44、TA 身上的这些优秀特质?

    45、你最想提升自己的哪一方面?对加班的看法 对工资的要求

    十二、Q&A

    Q:我只会 Go,能不能报 Java、C++ 的岗位?

    可以,做算法题时,可以选择自己熟悉的语言。跨语言投递岗位时,必须好好准备基础课程和算法,问得肯定会更深入。此外,最好能够对目标语言有一些基本了解,比如 Java 的锁/Map、C++ 的引用/指针/函数重载/虚函数、Golang 的协程/Channel 等,防止面试官问到语言相关的题目时无话可说。

    Q:手里有一个 offer ,要不要继续面试?

    有余力的情况下,多面试几家,得知自己的真实水平,也有和 HR 谈价的筹码。

    Q:一二面主要问什么?

    一二面主要考察候选人的专业能力和潜力。整体采用结构化面试,考察以下几项:

    基础课程:计算机网络、操作系统等,15~20 分钟 职能专项:语言、框架、工具等,15~20 分钟 算法能力:两道题,30~60 分钟 实习/项目经历:15 分钟 一二面是互相补充的关系,不是每轮面试都严格遵循上面的时间分配。一二面不是平行面。一面过了才能进二面,二面过了才能进三面。

    Q:三面/最后一面主要问什么?

    三面一般是经理面,包括专业能力和软素质两部分。专业能力是对前几轮面试的补充,主要是查漏补缺。更多的时间会考察候选人的软素质,包括学习能力/合作能力/解决问题的能力等,见附录 - 综合面试问题。这些问题完全可以提前准备,打好草稿。

    Q:我不喜欢的部门捞了我的简历怎么办?

    可以在约面试的时候拒绝,说明你想去另一个部门;同时想办法联系另一个部门的 HR 来捞你的简历。

    Q:转正是否真的都是白菜评级?

    “实习生转正一般都是白菜价”是一个常见的误区,身边有很多反例。影响转正评级的因素主要有两个:(1) 实习期间的产出;(2) 手里其他 offer 的水平。

    实习生转正是根据转正答辩来评级,而转正答辩主要是介绍自己实习期间的工作内容,所以转正评级实际上取决于实习期间的产出。影响产出的因素:

    1、部门水平。实习生过去大多是承担一些“杂活”,但核心部门的任务显然比普通部门质量更高,也更容易有可罗列的产出。

    2、个人水平。学习能力强、动手能力强,mentor / leader 就会分配一些相对更有挑战的工作。

    3、Mentor。实习生的工作基本上都是 mentor 分配的,有一个水平高且负责任的 mentor,工作的目标感和成就感会更强,产出事半功倍。

    4、主观能动性。主动要活儿,不划水;为团队做一些日常工作以外的贡献。“贡献”不一定非得是提升业务指标。如果入职后安装环境、熟悉业务、阅读源码的过程中踩了很多坑,那就整理一份新人手册吧,把经验和教训分享给后续新入职的同学。

    转正答辩后的评级并不是固定的。从收到转正意向书 (7~9 月) 到拿到带薪校招 offer (10~11 月),中间还有很长一段时间。HR 最终给出的薪资会综合转正评级与你手上的其他 offer 进行考虑。所以如果感觉自己转正评级不是很理想的话,可以尽可能多地争取几个其他公司的 offer。

    -------------------

    END

    --------------------

    我是王博Kings,985AI博士,华为云专家、CSDN博客专家(人工智能领域优质作者)。单个AI开源项目现在已经获得了2100+标星。现在在做AI相关内容,欢迎一起交流学习、生活各方面的问题,一起加油进步!

    我们微信交流群涵盖以下方向(但并不局限于以下内容):人工智能,计算机视觉,自然语言处理,目标检测,语义分割,自动驾驶,GAN,强化学习,SLAM,人脸检测,最新算法,最新论文,OpenCV,TensorFlow,PyTorch,开源框架,学习方法...

    这是我的私人微信,位置有限,一起进步!

    王博的公众号,欢迎关注,干货多多

    王博Kings的系列手推笔记(附高清PDF下载):

    博士笔记 | 周志华《机器学习》手推笔记第一章思维导图

    博士笔记 | 周志华《机器学习》手推笔记第二章“模型评估与选择”

    博士笔记 | 周志华《机器学习》手推笔记第三章“线性模型”

    博士笔记 | 周志华《机器学习》手推笔记第四章“决策树”

    博士笔记 | 周志华《机器学习》手推笔记第五章“神经网络”

    博士笔记 | 周志华《机器学习》手推笔记第六章支持向量机(上)

    博士笔记 | 周志华《机器学习》手推笔记第六章支持向量机(下)

    博士笔记 | 周志华《机器学习》手推笔记第七章贝叶斯分类(上)

    博士笔记 | 周志华《机器学习》手推笔记第七章贝叶斯分类(下)

    博士笔记 | 周志华《机器学习》手推笔记第八章集成学习(上)

    博士笔记 | 周志华《机器学习》手推笔记第八章集成学习(下)

    博士笔记 | 周志华《机器学习》手推笔记第九章聚类

    博士笔记 | 周志华《机器学习》手推笔记第十章降维与度量学习

    博士笔记 | 周志华《机器学习》手推笔记第十一章稀疏学习

    博士笔记 | 周志华《机器学习》手推笔记第十二章计算学习理论

    博士笔记 | 周志华《机器学习》手推笔记第十三章半监督学习

    博士笔记 | 周志华《机器学习》手推笔记第十四章概率图模型

    点分享

    点收藏

    点点赞

    点在看

    展开全文
  • 测试必须要知道的四个主要阶段 !

    千次阅读 2021-05-19 20:52:38
    一、测试主要的四阶段 1.测试计划设计阶段:产品立项之后,进行需求分析,需求评审,业务需求评级,绘制业务流程图。确定测试负责人,开始制定测试计划; 2.测试准备阶段:各成员编写测试用例、先小组内评审、后...
  • (一)数据库范围数据管理与数据库子系统的内容来自国家及地方50多年来所收集的专题图件、基础地理图形、遥感影像、生态监测、水文、社会经济以及多媒体等各类数据,其数据覆盖范围按照不同比例尺覆盖不同的范围,最大...
  • 下图是覆盖率在实际在项目中的主要实施点:分别详细介绍下:1.1 确定插桩方式Android项目只能使用JaCoCo的离线插桩方式。为什么?主要是因为Android覆盖率的特殊性:一般运行在服务器java程序的插桩可以在加载class...
  • Android 增量代码测试覆盖率工具实践

    热门讨论 2021-01-19 16:43:43
    当业务快速发展,新业务不断出现,开发同学粗心的情况下,难免会出现少测漏测的情况,如何保证新增代码有足够的测试覆盖率?当一段正常的代码,开发却修改了,测试人员没有测试其功能,如果保证能够发现? 所以代码...
  • 系统测试包含哪几部分?

    千次阅读 2021-07-23 05:07:31
    一、系统测试主要内容1、系统测试的过程:计划-->设计-->实现-->执行2、被测对象: xxxxxx系统3、测试被测对象的版本:例如V1.04、做的是什么测试: 系统测试 ,针对的是整个软件,采用黑盒测试5、依据:...
  • 本文目的:简单介绍所使用的工具示范一遍覆盖率流程得到代码文件→进行编译并插桩→跑程序→运行gcov、lcov,输出覆盖率可视化文件进一步说明细节内容参考文献A.基础内容:1.GCCGCC是一用于linux系统下编程的...
  • 而在中文领域,哈工大讯飞联合实验室也于昨日发布了基于覆盖的中文 BERT 预训练模型,在多中文数据集上取得了当前中文预训练模型的最佳水平,效果甚至超过了原版 BERT、ERINE 等中文预训练模型。基于 ...
  • 经过Z世代内容的不断发酵和滋长,B站形成了特有的“恰饭文化”,并推动其成为品牌目光聚集的红利场。 在用户流量增长和涨粉曝光的需求瓶颈之下,B站起飞应运而生。那么B站起飞要不要做?该怎么做?飞瓜数据(B站)...
  • (ps:java对视频解码支持并不完善,只能借助javacv完成,实际上javacv是封装了ffmpeg) 线程同步:这里5个线程之间需要协作同步,主要使用ReentrantLock锁来同步 画面显示:画面显示不需要特殊技术,重新Idea的...
  • 覆盖率工具会在仿真过程中收集信息,进行处理得到覆盖率报告,再根据报告找出覆盖之外的盲区,修改现有测试用例或者创建新的测试用例来填补这些盲区,这过程可以一直迭代进行,直到达到目标覆盖率 代码覆盖率 ...
  • Linux基本操作命令echo命令echo命令:把echo后面的内容显示... 文件名表示把内容覆盖原先的文件内容cat命令cat 常用选项-b 显示行编号(忽略空行)-n 显示所有行编号(包括空格)cat >>文件<cat >文件<&g...
  • route print 显示出IP路由,将主要显示网络地址Network addres,子网掩码Netmask,网关地址Gateway addres,接口地址Interface arp 查看和处理ARP缓存,ARP是名字解析的意思,负责把一IP解析成一物理性的MAC地址...
  • 本文代码实现基本按照《数据结构》课本目录顺序,外加大量的复杂算法实现,一篇文章足够。能换你一收藏了吧?
  • background-image: url() css上解决办法1.1.6 div里面的内容如何垂直居中1.1.7 RGBA1.1.8 块级元素1.1.9 行级元素1.1.10 行内块元素1.1.11 行高1.1.12 浮动1.2 Java Script1.2.1 Object.assig.
  • 让我们先来概览一下PaddleDetection2.0本次升级内容: 1、明星算法阵容: 新增超越YOLOv4、YOLOv5 的PP-YOLOv2,1.3M 超超超轻量目标检测算法PP-YOLO Tiny,全面领先同类框架的RCNN系列算法,以及SOTA 的Anchor ...
  • 牛逼!Java 从入门到精通,超汇总版

    万次阅读 多人点赞 2021-05-06 19:40:33
    Java 控制流程 Java 访问控制权限 面向对象 继承 封装 多态 面向对象最主要有三大特征:继承、封装和多态,你可以参考这篇博客 面向对象的三大基本特征和大基本原则 Java 抽象类和接口 ,也可以参考这篇文章 深入...
  • ih5快速入门(二)

    千次阅读 2020-12-19 19:09:06
    快速入门第二章,使用一简单案例来帮助大家,第一章所介绍的都会涉及到大家先浏览一下案例首先我们先看首页是如何做的,在这里会运用到动效事件同时也会在这里告诉大家音乐如何放进去,以及点击左上角可以使音乐...
  • 双方将依托融云提供 IM+RTC+X “”通信解决方案的实力,以及数美科技全栈式内容风控的技术背景,携手强化图文音视内容审核能力,共建互联网通讯行业内容新生态,为开发者的业务安全“保驾护航”。 文末有福利,...
  • 基于P2P的互联网内容加速

    千次阅读 2021-08-30 00:11:02
    大多数时候,定义问题比找到答案更难,也更有价值。这世界需要困难的、明确界定的问题。互联网所固有的问题是什么?可能是“内容交付”问题的不同方面,例如,客户端的内容加速,高质量的视频交付等到...
  • 2.简答 5题 3.综合 2题 重点看标记的,了解出小题,理解出小题或者大题,掌握出大题。 结合课本: 第一章概论 1.2软件工程 1.2.1软件工程定义(了解) 写出其中一种。 1.Friitz Bauer 在NATO会议上给出的定义 软件...
  • oracle索引类型及扫描方式大整理1.存储类型索引在各种关系型数据库系统中都是举足轻重的组成...本文主要就前6种索引进行分析。首先给出各种索引的简要解释:b*treeindex:几乎所有的关系型数据库中都有b*tree类型...
  • css - 如何拉伸背景图像以覆盖整个HTML元素?我正在尝试获取HTML元素(body,div等)的背景图像以拉伸其整个宽度和高度。没有太多运气。 除了作为背景图像之外,它是否可能或者我必须以其他方式做到这一点?我目前的...
  • 查看本次增量代码的覆盖率报告,输出报告到apk目录,供开发人员查看,由开发人员判断这个覆盖率是否合理。当然你也可以在低于 百分之多少 时抛出异常,中断打包。 框架的整体流程如下: 首先分为三块: 1、编译时:...
  • 编写中充分考虑到高职学生参加全网计算机等级考试一级msoffice的需求,对等级考试的内容进行了全面的覆盖,书中配有紧密结合等级考试内容的例题和习题,全书内容包括计算机基础知识、windowsxp操作系统、字处理软件...
  • 手机App测试,虽然要比Web测试复杂一些,但系统整理起来后,主要也就下面几方面: 一、App功能测试(也叫App手工测试) APP功能测试是指测试你这APP的所有功能点是否能够验证通过。APP功能测试的测试用例...
  • 二、云计算的 5 基本特征三、云计算的 4 种部署模型(一)私有云(二)社区云(三)公共云(四)混合云四、云计算的3种服务(一)基础架构即服务 (IaaS,Infrastructure as a Service)(二)平台即服务 (PaaS,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 143,046
精华内容 57,218
关键字:

五个全覆盖主要内容