【第09章】【单元测试】

netty 同时被 2 个专栏收录
13 篇文章 0 订阅
13 篇文章 1 订阅

【第09章-单元测试】


【博文目录>>>】


【工程下载>>>】


ChannelHandler 是Netty 应用程序的关键元素,所以彻底地测试它们应该是你的开发过程的一个标准部分。最佳实践要求你的测试不仅要能够证明你的实现是正确的,而且还要能够很容易地隔离那些因修改代码而突然出现的问题。这种类型的测试叫作单元测试。

虽然单元测试没有统一的定义,但是大多数的从业者都有基本的共识。其基本思想是,以尽可能小的区块测试你的代码,并且尽可能地和其他的代码模块以及运行时的依赖(如数据库和网络)相隔离。如果你的应用程序能通过测试验证每个单元本身都能够正常地工作,那么在出了问题时将可以更加容易地找出根本原因。
在本章中,我们将学习一种特殊的Channel 实现——EmbeddedChannel,它是Netty 专门为改进针对ChannelHandler 的单元测试而提供的。

因为正在被测试的代码模块或者单元将在它正常的运行时环境之外被执行,所以你需要一个框架或者脚手架以便在其中运行它。在我们的例子中,我们将使用JUnit 4 作为我们的测试框架,所以你需要对它的用法有一个基本的了解。如果它对你来说比较陌生,不要害怕;虽然它功能强大,但却很简单,你可以在JUnit 的官方网站(www.junit.org)上找到你所需要的所有信息。

你可能会发现回顾前面关于ChannelHandler 的章节很有用,因为这将为我们的示例提供素材。

9.1 EmbeddedChannel 概述


你已经知道,可以将ChannelPipeline 中的ChannelHandler 实现链接在一起,以构建你的应用程序的业务逻辑。我们已经在前面解释过,这种设计支持将任何潜在的复杂处理过程分解为小的可重用的组件,每个组件都将处理一个明确定义的任务或者步骤。在本章中,我们还将展示它是如何简化测试的。

Netty 提供了它所谓的Embedded 传输,用于测试ChannelHandler。这个传输是一种特殊的Channel 实现—EmbeddedChannel—的功能,这个实现提供了通过ChannelPipeline传播事件的简便方法。

这个想法是直截了当的:将入站数据或者出站数据写入到EmbeddedChannel 中,然后检查是否有任何东西到达了ChannelPipeline 的尾端。以这种方式,你便可以确定消息是否已经被编码或者被解码过了,以及是否触发了任何的ChannelHandler 动作。

表9-1 中列出了EmbeddedChannel 的相关方法。

这里写图片描述

入站数据由ChannelInboundHandler 处理,代表从远程节点读取的数据。出站数据由ChannelOutboundHandler 处理,代表将要写到远程节点的数据。根据你要测试的ChannelHandler,你将使用*Inbound()或者*Outbound()方法对,或者兼而有之。

图9-1 展示了使用EmbeddedChannel 的方法,数据是如何流经ChannelPipeline 的。你可以使用writeOutbound()方法将消息写到Channel 中,并通过ChannelPipeline 沿着出站的方向传递。随后,你可以使用readOutbound()方法来读取已被处理过的消息,以确定结果是否和预期一样。 类似地,对于入站数据,你需要使用writeInbound()和readInbound()方法。

这里写图片描述

在每种情况下,消息都将会传递过ChannelPipeline,并且被相关的ChannelInboundHandler 或者ChannelOutboundHandler 处理。如果消息没有被消费,那么你可以使用readInbound()或者readOutbound()方法来在处理过了这些消息之后,酌情把它们从Channel中读出来。

9.2 使用EmbeddedChannel 测试ChannelHandler

在这一节中,我们将讲解如何使用EmbeddedChannel 来测试ChannelHandler。
JUnit 断言

org.junit.Assert 类提供了很多用于测试的静态方法。失败的断言将导致一个异常被抛出,并将终止当前正在执行中的测试。导入这些断言的最高效的方式是通过一个import static 语句来实现:

import static org.junit.Assert.*;

一旦这样做了,就可以直接调用Assert 方法了:

assertEquals(buf.readSlice(3), read);

9.2.1 测试入站消息


图9-2 展示了一个简单的ByteToMessageDecoder 实现。给定足够的数据,这个实现将产生固定大小的帧。如果没有足够的数据可供读取,它将等待下一个数据块的到来,并将再次检查是否能够产生一个新的帧。

这里写图片描述

正如可以从图9-2 右侧的帧看到的那样,这个特定的解码器将产生固定为3 字节大小的帧。因此,它可能会需要多个事件来提供足够的字节数以产生一个帧。

最终,每个帧都会被传递给ChannelPipeline 中的下一个ChannelHandler。该解码器的实现,如代码清单9-1 所示。

// 代码清单9-1 FixedLengthFrameDecoder
// 扩展ByteToMessageDecoder 以处理入站字节,并将它们解码为消息
public class FixedLengthFrameDecoder extends ByteToMessageDecoder {
    // 指定要生成的帧的长度
    private final int frameLength;

    public FixedLengthFrameDecoder(int frameLength) {
        if (frameLength <= 0) {
            throw new IllegalArgumentException("frameLength must be a positive integer: " + frameLength);
        }
        this.frameLength = frameLength;
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // 检查是否有足够的字节可以被读取,以生成下一个帧
        while (in.readableBytes() >= frameLength) {
            // 从ByteBuf 中读取一个新帧
            ByteBuf buf = in.readBytes(frameLength);
            // 将该帧添加到已被解码的消息列表中
            out.add(buf);
        }
    }
}

现在,让我们创建一个单元测试,以确保这段代码将按照预期执行。正如我们前面所指出的,即使是在简单的代码中,单元测试也能帮助我们防止在将来代码重构时可能会导致的问题,并且能在问题发生时帮助我们诊断它们。

现在,让我们创建一个单元测试,以确保这段代码将按照预期执行。正如我们前面所指出的,即使是在简单的代码中,单元测试也能帮助我们防止在将来代码重构时可能会导致的问题,并且能在问题发生时帮助我们诊断它们。

代码清单9-2 展示了一个使用EmbeddedChannel 的对于前面代码的测试。

// 代码清单9-2 测试FixedLengthFrameDecoder
public class FixedLengthFrameDecoderTest {
    // 使用了注解@Test 标注,因此JUnit 将会执行该方法
    @Test
    // 第一个测试方法:testFramesDecoded()
    public void testFramesDecoded() {
        // 创建一个ByteBuf,并存储9 字节
        ByteBuf buf = Unpooled.buffer();
        for (int i = 0; i < 9; i++) {
            buf.writeByte(i);
        }
        ByteBuf input = buf.duplicate();
        // 创建一个EmbeddedChannel,并添加一个FixedLengthFrameDecoder,其将以3 字节的帧长度被测试
        EmbeddedChannel channel = new EmbeddedChannel(new FixedLengthFrameDecoder(3));

        // write bytes
        // 将数据写入EmbeddedChannel
        assertTrue(channel.writeInbound(input.retain()));
        // 标记Channel为已完成状态
        assertTrue(channel.finish());

        // read messages
        // 读取所生成的消息,并且验证是否有3 帧(切片),其中每帧(切片)都为3 字节
        ByteBuf read = (ByteBuf) channel.readInbound();
        assertEquals(buf.readSlice(3), read);
        read.release();

        read = (ByteBuf) channel.readInbound();
        assertEquals(buf.readSlice(3), read);
        read.release();

        read = (ByteBuf) channel.readInbound();
        assertEquals(buf.readSlice(3), read);
        read.release();

        assertNull(channel.readInbound());
        buf.release();
    }

    @Test
    // 第二个测试方法:testFramesDecoded2()
    public void testFramesDecoded2() {
        ByteBuf buf = Unpooled.buffer();
        for (int i = 0; i < 9; i++) {
            buf.writeByte(i);
        }
        ByteBuf input = buf.duplicate();

        EmbeddedChannel channel = new EmbeddedChannel(new FixedLengthFrameDecoder(3));
        // 返回false,因为没有一个完整的可供读取的帧
        assertFalse(channel.writeInbound(input.readBytes(2)));
        assertTrue(channel.writeInbound(input.readBytes(7)));

        assertTrue(channel.finish());
        ByteBuf read = (ByteBuf) channel.readInbound();
        assertEquals(buf.readSlice(3), read);
        read.release();

        read = (ByteBuf) channel.readInbound();
        assertEquals(buf.readSlice(3), read);
        read.release();

        read = (ByteBuf) channel.readInbound();
        assertEquals(buf.readSlice(3), read);
        read.release();

        assertNull(channel.readInbound());
        buf.release();
    }
}

9.2.2 测试出站消息


测试出站消息的处理过程和刚才所看到的类似。在下面的例子中,我们将会展示如何使用EmbeddedChannel 来测试一个编码器形式的ChannelOutboundHandler,编码器是一种将一种消息格式转换为另一种的组件。你将在下一章中非常详细地学习编码器和解码器,所以现在我们只需要简单地提及我们正在测试的处理器—AbsIntegerEncoder,它是Netty 的MessageToMessageEncoder 的一个特殊化的实现,用于将负值整数转换为绝对值。

该示例将会按照下列方式工作:

  • 持有AbsIntegerEncoder 的EmbeddedChannel 将会以4 字节的负整数的形式写出站数据;

  • 编码器将从传入的ByteBuf 中读取每个负整数,并将会调用Math.abs()方法来获取其绝对值;

  • 编码器将会把每个负整数的绝对值写到ChannelPipeline 中。

图9-3 展示了该逻辑。

这里写图片描述

代码清单9-3 实现了这个逻辑,如图9-3 所示。encode()方法将把产生的值写到一个List 中。

// 代码清单9-3 AbsIntegerEncoder
// 扩展MessageToMessageEncoder 以将一个消息编码为另外一种格式
public class AbsIntegerEncoder extends MessageToMessageEncoder<ByteBuf> {
    @Override
    protected void encode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // 检查是否有足够的字节用来编码
        while (in.readableBytes() >= 4) {
            // 从输入的ByteBuf中读取下一个整数,并且计算其绝对值
            int value = Math.abs(in.readInt());
            // 将该整数写入到编码消息的List中
            out.add(value);
        }
    }
}

代码清单9-4 使用了EmbeddedChannel 来测试代码。

// 代码清单9-4 测试AbsIntegerEncoder
public class AbsIntegerEncoderTest {
    @Test
    public void testEncoded() {
        // 创建一个ByteBuf,并且写入9 个负整数
        ByteBuf buf = Unpooled.buffer();
        for (int i = 1; i < 10; i++) {
            buf.writeInt(i * -1);
        }

        // 创建一个EmbeddedChannel,并安装一个要测试的AbsIntegerEncoder
        EmbeddedChannel channel = new EmbeddedChannel(new AbsIntegerEncoder());
        // 写入ByteBuf,并断言调用readOutbound()方法将会产生数据
        assertTrue(channel.writeOutbound(buf));
        // 将该Channel标记为已完成状态
        assertTrue(channel.finish());

        // read bytes
        // 读取所产生的消息,并断言它们包含了对应的绝对值
        for (int i = 1; i < 10; i++) {
            assertEquals((Integer) i, (Integer) channel.readOutbound());
        }
        assertNull(channel.readOutbound());
    }
}

下面是代码中执行的步骤。

(1) 将4 字节的负整数写到一个新的ByteBuf 中。

(2) 创建一个EmbeddedChannel,并为它分配一个AbsIntegerEncoder。

(3) 调用EmbeddedChannel 上的writeOutbound()方法来写入该ByteBuf。

(4) 标记该Channel 为已完成状态。

(5) 从EmbeddedChannel 的出站端读取所有的整数,并验证是否只产生了绝对值。

9.3 测试异常处理


应用程序通常需要执行比转换数据更加复杂的任务。例如,你可能需要处理格式不正确的输入或者过量的数据。在下一个示例中,如果所读取的字节数超出了某个特定的限制,我们将会抛出一个TooLongFrameException。这是一种经常用来防范资源被耗尽的方法。

在图9-4 中,最大的帧大小已经被设置为3 字节。如果一个帧的大小超出了该限制,那么程序将会丢弃它的字节,并抛出一个TooLongFrameException。位于ChannelPipeline 中的其他ChannelHandler 可以选择在exceptionCaught()方法中处理该异常或者忽略它。

这里写图片描述

其实现如代码清单9-5 所示。

// 代码清单9-5 FrameChunkDecoder
// 扩展ByteToMessageDecoder 以将入站字节解码为消息
public class FrameChunkDecoder extends ByteToMessageDecoder {
    private final int maxFrameSize;

    // 指定将要产生的帧的最大允许大小
    public FrameChunkDecoder(int maxFrameSize) {
        this.maxFrameSize = maxFrameSize;
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
            throws Exception {
        int readableBytes = in.readableBytes();
        // 如果该帧太大,则丢弃它并抛 出一个TooLongFrameException
        if (readableBytes > maxFrameSize) {
            // discard the bytes
            in.clear();
            throw new TooLongFrameException();
        }
        // 否则,从ByteBuf 中读取一个新的帧
        ByteBuf buf = in.readBytes(readableBytes);
        // 将该帧添加到解码消息的List 中
        out.add(buf);
    }
}

我们再使用EmbeddedChannel 来测试一次这段代码,如代码清单9-6 所示。

// 代码清单9-6 测试FrameChunkDecoder
public class FrameChunkDecoderTest {
    @Test
    public void testFramesDecoded() {
        // 创建一个ByteBuf,并向它写入9 字节
        ByteBuf buf = Unpooled.buffer();
        for (int i = 0; i < 9; i++) {
            buf.writeByte(i);
        }
        ByteBuf input = buf.duplicate();

        // 创建一个EmbeddedChannel,并向其安装一个帧大小为3 字节的FixedLengthFrameDecoder
        EmbeddedChannel channel = new EmbeddedChannel(new FrameChunkDecoder(3));

        // 向它写入2 字节,并断言它们将会产生一个新帧
        assertTrue(channel.writeInbound(input.readBytes(2)));
        try {
            // 写入一个4 字节大小的帧,并捕获预期的TooLongFrameException
            channel.writeInbound(input.readBytes(4));
            // 如果上面没有抛出异常,那么就会到达这个断言,并且测试失败
            Assert.fail();
        } catch (TooLongFrameException e) {
            // expected exception
        }

        // 写入剩余的2 字节,并断言将会产生一个有效帧
        assertTrue(channel.writeInbound(input.readBytes(3)));
        // 将该Channel 标记为已完成状态
        assertTrue(channel.finish());

        // Read frames
        // 读取产 生的消息,并且验证值
        ByteBuf read = (ByteBuf) channel.readInbound();
        assertEquals(buf.readSlice(2), read);
        read.release();

        read = (ByteBuf) channel.readInbound();
        assertEquals(buf.skipBytes(4).readSlice(3), read);
        read.release();
        buf.release();
    }
}

乍一看,这看起来非常类似于代码清单9-2 中的测试,但是它有一个有趣的转折点,即对TooLongFrameException的处理。这里使用的try/catch块是EmbeddedChannel的一个特殊功能。如果其中一个write*方法产生了一个受检查的Exception,那么它将会被包装在一个RuntimeException中并抛出,需要注意的是,如果该类实现了exceptionCaught()方法并处理了该异常,那么它将不会被catch块所捕获。这使得可以容易地测试出一个Exception是否在处理数据的过程中已经被处理了。这里介绍的测试方法可以用于任何能抛出Exception 的ChannelHandler 实现。

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

打赏
文章很值,打赏犒劳作者一下
相关推荐
<p style="font-size:16px;color:#666666;"> <img src="https://img-bss.csdn.net/202001311426171105.png" alt="" /> </p> <p style="font-size:16px;color:#666666;"> <strong><span style="font-size:20px;">课程目标</span></strong> </p> <p style="font-size:16px;color:#666666;"> 《从零开始学Scrapy网络爬虫》从零开始,循序渐进地介绍了目前流行的网络爬虫框架Scrapy。即使你没有任何编程基础,学习起来也不会有压力,因为我们有针对性地介绍了Python编程技术。另外,《从零开始学Scrapy网络爬虫》在讲解过程中以案例为导向,通过对案例的不断迭代、优化,让读者加深对知识的理解,并通过14个项目案例,提高学习者解决实际问题的能力。 </p> <p style="font-size:16px;color:#666666;"> <br /> </p> <p style="font-size:16px;color:#666666;"> <strong><span style="font-size:20px;">适合对象</span></strong> </p> <p style="font-size:16px;color:#666666;"> 爬虫初学者、爬虫爱好者、高校相关专业的学生、数据爬虫工程师。 </p> <p style="font-size:16px;color:#666666;"> <br /> </p> <p style="font-size:16px;color:#666666;"> <span style="font-size:20px;"><strong>课程介绍</strong></span> </p> <p style="font-size:16px;color:#666666;"> 《从零开始学Scrapy网络爬虫》共13。其中,第1~4为基础篇,介绍了Python基础、网络爬虫基础、Scrapy框架及基本的爬虫功能。第5~10为进阶篇,介绍了如何将爬虫数据存储于MySQL、MongoDB和Redis数据库中;如何实现异步AJAX数据的爬取;如何使用Selenium和Splash实现动态网站的爬取;如何实现模拟登录功能;如何突破反爬虫技术,以及如何实现文件和图片的下载。第11~13为高级篇,介绍了使用Scrapy-Redis实现分布式爬虫;使用Scrapyd和Docker部署分布式爬虫;使用Gerapy管理分布式爬虫,并实现了一个抢票软件的综合项目。 </p> <p style="font-size:16px;color:#666666;"> <span style="color:#FF0000;">      由于目标网站可能会对页面进行改版或者升级反爬虫措施,如果发现视频中的方法无法成功爬取数据,敬请按照页面实际情况修改XPath的路径表达式。视频教程主要提供理论、方法支撑。我们也会在第一时间更新源代码,谢谢!</span> </p> <p style="font-size:16px;color:#666666;"> <img src="https://img-bss.csdn.net/202001311426306665.png" alt="" /> </p> <p style="font-size:16px;color:#666666;"> <strong><span style="font-size:20px;">课程特色</span></strong> </p> <p style="font-size:16px;"> <img src="https://img-bss.csdn.net/202001311426415123.png" alt="" /> </p> <div> <br /> </div>
<div style="color:rgba(0,0,0,.75);"> <span style="color:#4d4d4d;"> </span> <div style="color:rgba(0,0,0,.75);"> <span style="color:#4d4d4d;"> </span> <div style="color:rgba(0,0,0,.75);"> <div style="color:rgba(0,0,0,.75);"> <span style="color:#4d4d4d;">当前课程中商城项目的实战源码是我发布在 GitHub 上的开源项目 newbee-mall (新蜂商城),目前已有 6300 多个 star,</span><span style="color:#4d4d4d;">本课程是一个 Spring Boot 技术栈的实战类课程,课程共分为 3 大部分,前面两个部分为基础环境准备和相关概念介绍,第三个部分是 Spring Boot 商城项目功能的讲解,让大家实际操作并实践上手一个大型的线上商城项目,并学习到一定的开发经验以及其中的开发技巧。<br /> 商城项目所涉及的功能结构图整理如下:<br /> </span> </div> <div style="color:rgba(0,0,0,.75);">   </div> <div style="color:rgba(0,0,0,.75);"> <p style="color:#4d4d4d;"> <img alt="modules" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3N0b3JlL25ld2JlZS1tYWxsLXMucG5n?x-oss-process=image/format,png" /> </p> </div> <p style="color:rgba(0,0,0,.75);"> <strong><span style="color:#e53333;">课程特色</span></strong> </p> <p style="color:rgba(0,0,0,.75);">   </p> <div style="color:rgba(0,0,0,.75);">   </div> <div style="color:rgba(0,0,0,.75);"> <ul> <li> 对新手开发者十分友好,无需复杂的操作步骤,仅需 2 秒就可以启动这个完整的商城项目 </li> <li> 最终的实战项目是一个企业级别的 Spring Boot 大型项目,对于各个阶段的 Java 开发者都是极佳的选择 </li> <li> 实践项目页面美观且实用,交互效果完美 </li> <li> 教程详细开发教程详细完整、文档资源齐全 </li> <li> 代码+讲解+演示网站全方位保证,向 Hello World 教程说拜拜 </li> <li> 技术栈新颖且知识点丰富,学习后可以提升大家对于知识的理解和掌握,可以进一步提升你的市场竞争力 </li> </ul> </div> <p style="color:rgba(0,0,0,.75);">   </p> <p style="color:rgba(0,0,0,.75);"> <span style="color:#e53333;">课程预览</span> </p> <p style="color:rgba(0,0,0,.75);">   </p> <div style="color:rgba(0,0,0,.75);">   </div> <div style="color:rgba(0,0,0,.75);"> <p style="color:#4d4d4d;"> 以下为商城项目的页面和功能展示,分别为: </p> </div> <div style="color:rgba(0,0,0,.75);"> <ul> <li> 商城首页 1<br /> <img alt="" src="https://img-bss.csdnimg.cn/202103050347585499.gif" /> </li> <li> 商城首页 2<br /> <img alt="" src="https://img-bss.csdn.net/202005181054413605.png" /> </li> <li>   </li> <li> 购物车<br /> <img alt="cart" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3QvY2FydC5wbmc?x-oss-process=image/format,png" /> </li> <li> 订单结算<br /> <img alt="settle" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3Qvc2V0dGxlLnBuZw?x-oss-process=image/format,png" /> </li> <li> 订单列表<br /> <img alt="orders" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3Qvb3JkZXJzLnBuZw?x-oss-process=image/format,png" /> </li> <li> 支付页面<br /> <img alt="" src="https://img-bss.csdn.net/201909280301493716.jpg" /> </li> <li> 后台管理系统登录页<br /> <img alt="login" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3QvbWFuYWdlLWxvZ2luLnBuZw?x-oss-process=image/format,png" /> </li> <li> 商品管理<br /> <img alt="goods" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9uZXdiZWUtbWFsbC5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vcG9zdGVyL3Byb2R1Y3QvbWFuYWdlLWdvb2RzLnBuZw?x-oss-process=image/format,png" /> </li> <li> 商品编辑<br /> <img alt="" src="https://img-bss.csdnimg.cn/202103050348242799.png" /> </li> </ul> </div> </div> </div> </div>
<p style="color:#333333;"> <strong> </strong> </p> <p style="font-family:"color:#222226;font-size:14px;background-color:#FFFFFF;"> <strong><span style="color:#337FE5;">[为什么要学习Spring Cloud微服务]</span> </strong> </p> <p style="font-family:"color:#222226;font-size:14px;background-color:#FFFFFF;"> <strong><span style="color:#4D555D;"></span> </strong> </p> <p class="ql-long-24357476" style="font-family:"color:#222226;font-size:14px;background-color:#FFFFFF;"> <strong><span style="font-family:"background-color:#FFFFFF;">SpringCloud作为主流微服务框架,<span style="color:#4D555D;">已成为各互联网公司的首选框架,国内外企业占有率持续攀升,</span>是Java工程师的必备技能。</span><span style="font-family:"background-color:#FFFFFF;">就连大名鼎鼎的阿里巴巴</span><span style="font-family:"background-color:#FFFFFF;">dubbo</span><span style="font-family:"background-color:#FFFFFF;">也正式更名为</span><span style="font-family:"background-color:#FFFFFF;">Spring Cloud Alibaba</span><span style="font-family:"background-color:#FFFFFF;">,成为了</span><span style="font-family:"background-color:#FFFFFF;">Spring Cloud </span><span style="font-family:"background-color:#FFFFFF;">微服务中的一个子模块。</span><span style="font-family:"background-color:#FFFFFF;"></span><span style="font-family:"background-color:#FFFFFF;">Spring Cloud是企业架构转型、个人能力提升、架构师进阶的不二选择。</span> </strong> </p> <p style="color:#333333;"> <strong><strong><br /> </strong> </strong> </p> <strong><span style="font-family:"color:#337FE5;font-size:14px;background-color:#FFFFFF;">【推荐你学习这门课的理由】</span><br /> </strong> <p> <br /> </p> <p> <span>1、</span><span style="color:#222226;font-family:"font-size:14px;background-color:#FFFFFF;">本课程总计</span><span style="background-color:#FFFFFF;">29</span><span style="color:#222226;font-family:"font-size:14px;background-color:#FFFFFF;">课时,<span style="color:#333333;">从微服务是什么、能够做什么开始讲起,绝对的零基础入门</span></span><span></span> </p> <p> <span style="background-color:#FFFFFF;">2、<span style="color:#333333;">课程附带全部26个项目源码,230页高清PDF正版课件</span><span style="color:#333333;"></span></span> </p> <p> <span style="background-color:#FFFFFF;"><b><br /> </b></span> </p> <p> <span style="background-color:#FFFFFF;"><b><span style="color:#337FE5;">【课程知识梳理】</span></b></span> </p> <p> <span style="background-color:#FFFFFF;"><b>1、</b></span><span style="color:#333333;">先讲解了什么是单体架构、什么是微服务架构、他们之间有什么区别和联系,各自有什么优缺点。</span> </p> <p> <span style="color:#333333;">2、</span><span style="color:#333333;">从本质入手,使用最简单的Spring Boot搭建微服务,让你认清微服务是一种思想和解决问题的手段,而不是新兴技术。</span> </p> <p style="color:#333333;"> 3、讲解Spring Boot 与Spring Cloud 微服务架构之间的联系,原生的RestTemplate工具,以及Actuator监控端点的使用。 </p> <p style="color:#333333;"> 4、带着微服务所带来的各种优缺点,为大家引入服务发现与注册的概念和原理,从而引入我们的第一个注册中心服务Eureka。 </p> <p style="color:#333333;"> 5、引入负载均衡的理念,区分什么是服务端负载均衡,什么是客户端负载均衡,进而引入Ribbon负载均衡组件的详细使用。 </p> <p style="color:#333333;"> 6、为了解决微服务之间复杂的调用,降低代码的复杂度,我们引入了Feign声明式客户端,让你几行代码搞定服务的远程调用。 </p> <p style="color:#333333;"> 7、最后为大家介绍了整个微服务体系应该包含什么,学习路线是什么,应该学习什么。 </p> <p style="color:#333333;"> <strong><br /> </strong> </p> <p style="color:#333333;"> <strong><span style="color:#337FE5;">【</span><strong><span style="color:#337FE5;">学习方法</span></strong><span style="color:#337FE5;"></span><span style="color:#337FE5;">】</span></strong> </p> <p style="color:#333333;"> 每一节课程均有代码,最好的方式是静下心来,用一天的时间,或者两个半天时间来学习。 </p> <p style="color:#333333;"> 一边听我的讲解,一边使用我提供的项目代码进行观察和运行。 </p> <p style="color:#333333;"> 只要你能跟住我的节奏,你就可以搞定微服务。 </p> <br />
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页

打赏

Wang-Junchao

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值