--- title: "13-项目笔记(六)" created: 2025-12-02 tags: - 项目 aliases: - 项目笔记(六) --- # 项目笔记(六) ## 一、项目概述 本项目是一个面向开发者的 API 平台,提供 API 接口供开发者调用。用户通过注册登录,可以开通接口调用权限,并可以浏览和调用接口。每次调用都会进行统计,用户可以根据统计数据进行分析和优化。管理员可以发布接口、下线接口、接入接口,并可视化接口的调用情况和数据。本项目侧重于后端,涉及多种编程技巧和架构设计层面的知识。 ## 二、本期时间点 ![[Vy5Kg2k0oI24DDWU-8ca183ae.webp]] ## 三、本期计划(时间点 **06:33-07:59**) 1. 补充完整网关的业务逻辑(怎么去操作数据库、怎么复用之前的方法?RPC) 2. 完善系统、开发一个监控统计功能 ## 四、网关业务逻辑(时间点 **07:59-**23:53) **问题:**网关项目比较纯净,没有操作数据库的包、并且还要调用我们之前写过的代码,复制粘贴维护麻烦。 **理想:**直接请求到其他项目的方法。 我们的网关项目遇到一个问题,就是网关项目比较纯净,没有涉及数据库操作的包,但同时需要调用之前我们编写过的代码。尽管复制粘贴一开始并不麻烦,但是随着次数增多以及未来的修改维护,就变得相当繁琐了。 因此,我们的理想情况就是希望能够直接请求 yuapi-backend 项目中的 invokeCount 方法,怎么做呢?在这里,涉及到一个知识点,就是远程过程调用(RPC)。 ### RPC 讲解(时间点 09:14-23:53) 简单讲一下这个 RPC 是什么,假设你之前没有听说过 RPC,那么想象一下情景: 你在项目 A 中编写了一个非常有用的函数,现在你在项目 B 中也想要使用这个函数。但问题是,项目 A 和项目 B 是独立运行的,它们不共享同一片内存,也不在同一个进程中。那么,你怎么做才能调用项目 A 中的那个函数呢? #### 1. 怎么调用其他项目的方法?(时间点 09:34-11:41) 1. 复制代码和依赖、环境 2. HTTP 请求(提供一个接口,供其他项目调用) 3. RPC 4. 把公共的代码打个 jar 包,其他项目去引用(客户端 SDK) **进一步说明:** 首先是直接复制粘贴别的项目的方法,然而这可能引发环境依赖和代码问题,因为项目之间各自有独特的设置和条件。另一个方法是使用 HTTP 请求,例如,在我们的 yuapi-backend 项目中,有一个 invokeCount 的方法,我们可以将其写到 controller 中并为其创建一个 API 接口,以便其他项目可以调用,这个方法是可行的,但同样需要注意它的限制和适用性。 除此之外,还有其他一些方法,例如 RPC 和微服务等。尤其是 RPC,它与 HTTP 请求的区别是面试中的一个常见问题,稍后会简要解释。此外,还有一种常见的方法是将公共代码打包成一个 jar 包,供其他项目引用。这种方法在 API 开放平台项目中很常见,就像 yuapi-client-sdk 项目的做法一样,将其打包成 jar 包,yuapi-backend 项目和 yuapi-gateway 项目都了引用这个 jar 包。 #### 2. HTTP 请求怎么调用?(时间点 11:45-12:36) 1. 提供方开发一个接口(地址、请求方法、参数、返回值) 2. 调用方使用 HTTP Client 之类的代码包去发送 HTTP 请求 #### 3. RPC (时间点 12:36-23:53) **作用:**像调用本地方法一样调用远程方法。 **和直接 HTTP 调用的区别:** 1. 对开发者更透明,减少了很多的沟通成本。 2. RPC 向远程服务器发送请求时,未必要使用 HTTP 协议,比如还可以用 TCP / IP,性能更高。(内部服务更适用)。 **RPC 调用模型:** ![[Q3KrXpOczNMdg1ZD-a8dd5110.webp]] **进一步说明:** RPC 和 HTTP 请求在本质上有一些区别。RPC 相对于 HTTP 请求更加透明,这意味着它在开发者间更具透明度。**这种透明性是如何理解的呢?**一个广泛的定义是,RPC 的目标是使远程方法的调用就像调用本地方法一样,从而实现更为便捷的远程通信。 以 HTTP 请求为例,调用方需要完成诸多步骤。首先,它需要发送 HTTP 请求,使用一个 HTTP 客户端的代码库。以我们之前提到的 yuapi-client-sdk 项目为例,我们可以看到客户端对象是如何使用 HTTPUtil 这个 hutool 工具类的包来发送请求的,这意味着调用方需要编写特定的代码段来发送请求,然后处理返回值,可能还需要解析返回值并封装参数的映射关系等。 ![[y4Fvmu1zYMebf6o1-ae1257e6.webp]] 在调用本地方法时,我们可以直接传递参数给方法,比如接口统计的方法(如下图所示)。我们之前提到的 yuapi-client-sdk 项目里,你需要自行封装 HTTP 请求,将参数打包成一个参数映射(map),然后按照客户端工具类的方式发送请求。此外,你还需要解析返回值,将其中的 code、data 以及 message 等信息提取出来。 ![[BwZUHA2J0kwdHLgq-42bf06cc.webp]] 但是,如果我们使用 RPC 方式,就能够实现与调用本地方法类似的体验。你可以直接指定要传递的参数,并且也能够直接获得返回值,无论是布尔类型还是字符串。RPC 方式不需要像 HTTP 请求那样进行额外的封装,但如果你需要封装,当然也可以根据需求自行处理。 因此,我们可以说 RPC 的主要目标之一是模仿调用本地方法的方式,从而实现远程调用。比方说,现在在 yuapi-backend 项目中,有一个方法写在了这个项目里,那么我们只需一行代码:userInterfaceInfoService.invokeCount,就可以调用该方法。 ![[BwZUHA2J0kwdHLgq-42bf06cc.webp]] ![[p74GYZGBP68MFUsd-19dc142a.webp]] 同样的,如果切换到另一个项目,比如 yuapi-gateway,在这个网关项目中,我们同样可以直接使用这行代码,做到方法调用的无缝切换。 ![[KmyAOAZZgzPrw2QG-dc84dd59.webp]] RPC 的主要职责就是这个,它的最大作用在于模拟本地方法调用的体验。看上去是请求本地代码,实际上,它可能会请求到其他项目、其他服务器等等。这就是 RPC 的最大价值所在。如果大家不理解,可以想一下刚刚的例子,你只需知道 RPC 最大的优势在于它的透明性,你不需要了解它是如何在 HTTP Client 中怎么封装参数,只需直接调用现成的方法即可,这样可以大大减少沟通成本。 --- 💡 有同学问:**feign 不也是动态生成的 httpclient 吗?** Feign 本质上也是动态生成的 HTTP 客户端,但它在调用远程方法时更加精简了 HTTP 请求的步骤。尽管 Feign 使远程调用看起来像是调用本地方法,但实际上与 RPC 仍然有一些微小的区别。虽然两者都可以实现类似的功能,但它们在底层协议上存在差异。 RPC(Remote Procedure Call 远程过程调用) 的一个关键优势在于,它可以使用多种底层协议进行远程调用,而不限于 HTTP 协议。虽然 HTTP 协议可以实现类似的功能,但考虑到性能,RPC 可以选择更原生的协议,如 TCP/IP。而且,网络上还存在其他性能更高的协议,可以根据需要进行选择。 在微服务项目中,对于内部接口,使用 RPC 可能会获得更好的性能。然而,选择使用 Feign 还是 RPC 取决于具体的技术需求,没有绝对的优劣之分。需要注意的是,RPC 和 HTTP 请求可以结合使用,因为 RPC 的底层协议可以是 HTTP,也可以是其他协议,如 TCP/IP 或自定义协议。 综上所述,RPC 和 HTTP 请求是可以互相结合的,但 RPC 在协议的选择上更加灵活。当面试被问到这方面的问题时,首先强调 RPC 的主要作用,然后阐述 RPC 和 HTTP 之间的关系。 --- 让我们简单绘制一张图,来说明 RPC 的工作流程。我们刚刚已经了解了 RPC 的作用以及为什么需要使用 RPC,现在详细说明实现类似本地方法调用的方式来调用远程方法。这需要我们的两个项目,即方法提供者项目和方法调用者项目,来进行一些特定的操作。 在这里,引入一个简单的概念,对于学习过操作系统的同学应该很熟悉。为了实现 RPC,我们需要涉及几个关键角色。 \1. **提供者(Producer/Provider):** 首先,我们需要一个项目来提供方法,这个项目被称为提供者。它的主要任务是为其他人提供已经写好的代码,让其他人可以使用。举例来说,我们可以提供一个名为 invokeCount 的方法。 \2. **调用方(Invoker/Consumer):** 一旦服务提供者提供了服务,调用方需要能够找到这个服务的位置。这就需要一个存储,用于存储已提供的方法,调用方需要知道提供者的地址和 invokeCount 方法,这里需要一个公共的存储。 \3. **存储:** 这是一个公共存储,用于存储提供者提供的方法信息。调用方可以从这个存储中获取提供者的地址和方法信息,例如,提供者的地址可能是 123.123.123.1,而方法是 invokeCount,这些信息会存储在这个存储器中。 调用方可以从存储中获取信息后,就知道调用 invokeCount 方法时需要访问 123.123.123.1,这就是 RPC 的基本流程,这三个角色构成了整个 RPC 模型。 存储有时也会被称为**注册中心**,它管理着服务信息,包括提供者的 IP 地址等等。调用方从这里获取信息,以便进行调用。 ![[Q3KrXpOczNMdg1ZD-a8dd5110.webp]] 然而,需要注意的是在整个流程中,最终的调用并不是由注册中心来完成的。虽然注册中心会提供信息,但实际上调用方需要自己进行最后的调用动作。注册中心的作用是告诉调用方提供者的地址等信息,然后调用方会根据这些信息来完成最后的调用。 一般情况下,调用方会直接寻找提供者进行调用,而不是依赖注册中心来完成实际的调用过程。注册中心主要的功能是提供地址信息,而并不会承担将调用方所需的内容传送到提供者的角色,整个过程遵循这样的流程。 ## 五、Dubbo 框架 (时间点 23:53-02:04:23) ### 1. Dubbo 框架介绍 接下来,我们已经建立了整个模型的基本理解,那么我们应该如何实现这一切呢? 建议大家去学习一下 Dubbo 框架,它是目前国内非常主流的 RPC 实现框架。当然,还有其他类似的框架,比如 GRPC 和 TRPC 等等,这些都是不同的 RPC 远程调用框架,Dubbo 和 GRPC 是比较知名的。Dubbo 由阿里开发,而 GRPC 是由 Google 开发,TRPC 则是由腾讯开发。 Dubbo 框架在国内学习起来可能更容易些。有同学可能会问,**Dubbo 框架需要花多少时间学习**?并不需要花费很多时间。如果你只是想入门,跟着直播回放就足够了。理解了 Dubbo 框架的基本思想后,实际使用起来只需要几行代码。当然,在使用过程中可能会遇到一些小问题,但是🐟会带大家克服这些问题,所以不用担心。 最佳的学习方式是阅读 Dubbo 框架的官方文档,会帮助你更好地理解这个框架。—— [Dubbo 框架官方文档](https://dubbo.incubator.apache.org/zh/docs3-v2/java-sdk/quick-start/spring-boot/) **两种使用方式:** 1. Spring Boot 代码(注解 + 编程式):写 Java 接口,服务提供者和消费者都去引用这个接口。 2. IDL(接口调用语言):创建一个公共的接口定义文件,服务提供者和消费者读取这个文件。优点是跨语言,所有的框架都认识。 Dubbo底层用的是 Triple 协议:[Triple 协议](https://dubbo.incubator.apache.org/zh/docs3-v2/java-sdk/concepts-and-architecture/triple/)。 **示例项目学习:** zookeeper 注册中心:通过内嵌的方式运行,更方便。 **启动流程:**最先启动注册中心,先启动服务提供者,再启动服务消费者。 **整合运用:** 1. backend 项目作为服务提供者,**提供 3 个方法:** 2. 1. 实际情况应该是去数据库中查是否已分配给用户 2. 从数据库中查询模拟接口是否存在,以及请求方法是否匹配(还可以校验请求参数) 3. 调用成功,接口调用次数 + 1 invokeCount 3. gateway 项目作为服务调用者,调用这 3 个方法 ps.**(时间点 52:17-****01:47:53****跳过)**官方提供的代码和文档内容,与回放几乎完全不一致了。大致了解下即可,🐟用 Zookeeper 改依赖改一个小时多,还是没成功,所以我们直接跳过使用 Zookeeper,用 Nacos ╰( ̄▽ ̄)╭ **期间讲解的知识点:** 1. **Zookeeper 是什么呢?** 有同学学过的话,就知道 Zookeeper 就是我们之前图中所提到的注册中心。Dubbo 的作用就是将你的提供者、调用方和注册中心这三者通过其框架整合在一起,从而完成整个远程过程调用(RPC)。因此,Dubbo 可以被视为一个实现了 RPC 功能的框架,而 Zookeeper 则是一个可作为注册中心的存储, Nacos 也可以作为注册中心**。** ![[Q3KrXpOczNMdg1ZD-a8dd5110.webp]] 1. **为什么一定要知道注册中心的地址?** 因为我们的服务提供者需要将自己提供的接口方法告知注册中心,所以它必须知道注册中心的地址,这样才能将自己的信息上报给注册中心,这也是为什么我们必须要配置注册中心。同样的道理,我们的消费者调用方也需要知道注册中心的地址,以便将注册中心的地址配置到项目中。 1. **IDL 是什么呢?** IDL(接口定义语言)是一种约定俗成的语言,用于定义接口和数据结构的语法。它是一种人为约定的语言,通过这种语法,可以明确地定义接口和数据的结构,使各方在交流和协作时能够达成一致。这种约定的语言为不同的系统、平台或语言提供了一种统一的描述方式,使得不同环境下的应用程序能够理解和交互。 ### 2. Nacos (时间点 01:47:53-02:04:23) #### 2.1 快速开始(时间点 01:47:53-01:49:52) 创建一个 code 目录。 ps.之前已经强调过,在使用各种框架和库时,尽量避免将项目存放在包含中文字符的路径下。一定要注意,路径名称不能包含中文字符,因为如果不小心将项目放在中文路径下,可能会导致各种莫名其妙的错误出现。 ![[KuauDYBeaB4dZtBn-c68970e3.webp]] 把 yuapi-backend 项目拖到 code 目录下。 ![[qazzcRcWiuQVwAOh-54682d2f.webp]] 然后查看 Nacos 的官方文档。 —— [Nacos 官方文档](https://nacos.io/zh-cn/docs/quick-start.html) ![[YHv1KMpug1jSaEbr-bb45cf6e.webp]] 往下滑,根据提示下载压缩包。 ![[x8ZPXlq7MR7mZnKu-524f7979.webp]] 来到 github,找到官方推荐的稳定版本 2.1.1。 ![[b7aF5KzQhW1PcHhv-36fafe28.webp]] ```text https://github.com/alibaba/nacos/releases/download/2.1.1/nacos-server-2.1.1.zip ``` 用 macOS M1 的 xdm 要使用 1.4.2 版本,来自鱼友小王提供的解决方法~~( ̄▽ ̄)~~ ![[HeG3HdeUIHCVyXcF-c9f50a38.webp]] ```text https://github.com/alibaba/nacos/releases/download/1.4.2/nacos-server-1.4.2.zip ``` 相关博客:[macbook pro m1 芯片,2021版本安装nacos报错踩坑](https://blog.csdn.net/asd991543753/article/details/127615419) ![[ULphup04fVRqNlXh-9380828e.webp]] macOS M1 的 yuapi-backend、yuapi-gateway 项目的依赖设置为: ![[YPuJij6imOlRKiQ7-77449216.webp]] ![[YtRJGykKBhFrqXKh-15f5366f.webp]] 下载完成后,把它解压到 D 盘; 解压完后,把压缩包删掉(它没用了.jpg)。 ![[kyKfX5jgAJnzms3m-35f2ba38.webp]] 这里说 2.2.0.1、2.2.1 版本必须修改配置文件,其他版本建议设置,我们是 2.1.1,跳过,看下一个。 ![[ilHzfw65dShxXAt3-24728f4a.webp]] 下一步,它告诉了我们启动服务器的命令,复制命令。 ![[5RBqGtLbFh3Fo1kO-2b502a68.webp]] ```text # Linux/Unix/Mac sh startup.sh -m standalone # ubuntu bash startup.sh -m standalone # Windows startup.cmd -m standalone ``` 进入 bin 目录,选中上面的路径栏,输入 cmd,按[Enter]回车进入。 ![[mvehdMisKjxgsRod-6cc7fb23.webp]] 把命令粘贴进去,先以单机模式运行,在 8848 端口启动起来了。 ps.别关掉,按右上角的最小化。 ![[Hde2gEV9PNVf8sw6-623e351a.png]] ![[ZeDsVFcBw781Uz5H-cfb3ee7e.webp]] #### 2.2 整合 Nacos 注册中心(时间点 01:49:52-02:01:24) 看一下 Dubbo 怎么用 Nacos。 —— [Dubbo 配置 Nacos 文档](https://dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/registry/nacos/) ![[kOkkLThHmyEYrLmJ-0aef640d.webp]] 要引入依赖,复制依赖。 ```xml org.apache.dubbo dubbo 3.0.9 com.alibaba.nacos nacos-client 2.1.0 ``` 粘贴依赖并加载。 ![[NHNpNdJhLdU1rOhN-eeff0ee1.webp]] 回到 Dubbo 文档,往下滑,它这里提示 Dubbo 3.0.0 及以上版本需要 nacos-client 2.0.0 及以上版本。 ![[LPK6xQIoDjO8YC3W-2471a93c.webp]] 继续往下滑,要进行配置了,复制配置(它这个配置还少了":")。 ![[Oy6BI47KrQERShZK-cb2cea99.webp]] 这个配置还要补充点信息,不然它注册不了。 ps.因为它只配置了注册中心的地址,但没有提供其他必要的配置信息,例如应用名称、协议、服务提供者/消费者等。Dubbo 的配置需要包含完整的信息,以便正确地启动和配置服务。 ```text # 以下配置指定了应用的名称、使用的协议(Dubbo)、注册中心的类型(Nacos)和地址 dubbo: application: # 设置应用的名称 name: dubbo-springboot-demo-provider # 指定使用 Dubbo 协议,且端口设置为 -1,表示随机分配可用端口 protocol: name: dubbo port: -1 registry: # 配置注册中心为 Nacos,使用的地址是 nacos://localhost:8848 id: nacos-registry address: nacos://localhost:8848 ``` 粘贴到这里,点击启动。 ![[IjpmHALgpkaLQjMI-f4abefe0.webp]] 把 redis 启动后,启动项目。 ![[994CDJArSwSbwsJk-95181cca.png]] 然后把 yuapi-gateway 项目拖到 yuapi-backend 项目中,方便管理。 ![[yPlL3SvE7O867ZGR-288763d5.webp]] 把 yuapi-gateway 项目用 idea 单独打开。 ![[tAW44px0UgQq8326-4d6709e9.webp]] yuapi-backend 项目启动成功之后,我们把配置也粘贴到 yuapi-gateway 项目中。 ![[sDN8CStLyq5aAZoL-12c66319.webp]] 再把依赖引进 yuapi-gateway 项目中,点击加载。 ![[mZ5v1Dc5fWV2PrQM-a3b3a69c.webp]] 点击启动,启动成功。 ![[vOah4eKvTLfwJLJo-b851cd16.png]] 我们去 Nacos 注册中心看一下,访问 [http://自己服务的](http://xn--zfr16x5khr6p0nn/) IP 地址:8848/nacos/index.html。 ![[oHFgUBu6ZLn8tTDk-35eb420f.webp]] 默认用户名和密码都是`nacos`。 ![[1y2pVF8bC59ZF2kV-1f393aed.webp]] 我们现在还没有整合示例,所以这里没有服务。 ![[5sg7lU2s4qOhyFpr-31caf03d.webp]] #### 2.3 整合示例 先停止这两个项目的运行。 ![[HyqodZznI1gjER6Y-690ed3ec.png]] ![[sFZKKrrBqb5SZU8Z-93764577.png]] ps.本来🐟讲解 Zookeeper,用了官方的示例,不过它更新的着实快,代码更新,文档也更新🤣我们直接去🐟那,把示例代码下载下来(嘿嘿.jpg) —— [本期后端项目代码](http://gitlab.code-nav.cn/root/yuapi-backend/-/tree/c52760d51aa0aaaf04acb52837627d111ecbd4bc/) ![[aMYijNsRhNCndlHQ-f048b456.webp]] 解压压缩包,进入解压后的项目。 ps.为啥不直接替换掉自己的,咳咳,有点怕🐟设置的依赖和自己的依赖冲突🐶 这翻车时长(瑟瑟发抖.jpg) ![[z5JXDUGCEQBGE570-410f42cd.webp]] 复制 provider 目录。 ![[Hq1SwibkzdiRTqNP-a657ec0e.webp]] 粘贴到 yuapi-backend 项目中。 ![[zI7MnSiPISdsRGqd-59ef6734.webp]] 在 yuapi-backend 项目中的 MyApplication 添加注解。 ![[kopNP5WyJZv6e4zf-b5639a2c.webp]] 复制 project 目录。 ![[37mSp14SebMuppjr-8060f112.webp]] 粘贴到 yuapi-gateway 项目中,消费方和服务方都需要 DemoService。 ![[KTzeE1Iuz1jVUyut-ecdd89a0.webp]] 复制 YuapiGatewayApplication.java 的代码。 ![[kHQXjAq6S1294YVE-4d84d544.webp]] 替换掉 yuapi-gateway 项目中 YuapiGatewayApplication.java 的代码。 ![[voLYBE2mKsw0WoUA-6e30c8c4.webp]] ```java package com.yupi.yuapigateway; import com.yupi.project.provider.DemoService; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.stereotype.Service; @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class}) @EnableDubbo @Service public class YuapiGatewayApplication { @DubboReference private DemoService demoService; public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(YuapiGatewayApplication.class, args); YuapiGatewayApplication application = context.getBean(YuapiGatewayApplication.class); String result = application.doSayHello("world"); String result2 = application.doSayHello2("world"); System.out.println("result: " + result); System.out.println("result: " + result2); } public String doSayHello(String name) { return demoService.sayHello(name); } public String doSayHello2(String name) { return demoService.sayHello2(name); } // @Bean // public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { // return builder.routes() // .route("tobaidu", r -> r.path("/baidu") // .uri("https://www.baidu.com")) // .route("toyupiicu", r -> r.path("/yupiicu") // .uri("http://yupi.icu")) // .build(); // } } ``` 启动项目。 ![[uBbQiQ48fAfmENco-d457fc88.png]] ![[0yS85J50kpJkvFvn-21e7a91f.webp]] yuapi-gateway 项目报错了,看看报什么错,第一个是因为缓存,这个不用在意,看下一个报错。 ![[HDJkxa8tPh3VBkEf-9d20a4bd.webp]] 第二个是端口被占用,它这边服务自动绑定 22222 端口,但是绑定失败。 ![[bgRQdmAmZeq2Y0nc-9504ec27.webp]] yuapi-backend 项目可以启动成功,yuapi-gateway 项目却报错了,还是端口占用,我们也没设置多少端口,怎么就占用了呢?(挠头.jpg) --- **思考过程:** 按照报错信息,我们应该要解除占用端口的服务,解除之后,发现 yuapi-backend 项目停止启动了。 原来,因为我们写 dubbo 端口配置是`-1`(随机端口),但是这个系统啊,竟然把两个项目都随机成相同的端口🤣 所以我们只需把这两个项目端口配置的端口号定死即可。 --- 把 yuapi-backend 项目的端口设置成 22221,yuapi-gateway 项目设置成 22223; 然后把 yuapi-backend 项目和 yuapi-gateway 项目关闭,然后再打开。 ps.为什么关闭呢?因为修改之后,发现它又默认在 22222 端口启动,可能有缓存的原因,关闭再打开就没事。 ![[LZRmekIVxGOu1HKV-7df5bb5f.webp]] ![[ua5JJqvCR8kZsTP7-7f4cb420.webp]] 启动 yuapi-backend 项目之后, 再启动 yuapi-gateway 项目。 ![[HyqodZznI1gjER6Y-690ed3ec.png]] ![[sFZKKrrBqb5SZU8Z-93764577.png]] 控制台成功输出结果,有报错没关系,能输出结果就行。 ![[LhpPy9SxMmSalHt3-1b0fdbcf.webp]] ![[dUYvokKbvijo5stu-14d87acc.webp]] 去 Nacos 注册中心看一下,访问 [http://自己服务的](http://xn--zfr16x5khr6p0nn/) IP 地址:8848/nacos/index.html,注册上了。 ![[nKPi9iqGdjYIxDRK-c84a55ab.webp]] 拜拜咯(ㄏ ̄▽ ̄)ㄏbye #### 2.4 问题总结(时间点 02:01:24-02:04:23) **注意:** 1. 服务接口类必须要在同一个包下,建议是抽象出一个公共项目(放接口、实体类等) 2. 设置注解(比如启动类的 EnableDubbo、接口实现类和 Bean 引用的注解) 3. 添加配置 4. 服务调用项目和提供者项目尽量引入相同的依赖和配置 --- **项目分区导航**:⬅️ [[12-项目笔记(五)|12-项目笔记(五)]] | 13-项目笔记(六) | ➡️ [[14-项目笔记(四)|14-项目笔记(四)]]