Dubbo框架实现RPC远程调用包括nacos的配置和初始化

项目背景介绍

这个技术我是直接在项目中运用并且学习的,所以我写笔记最优先的角度就是从项目背景出发

继上一次API网关完成了这个实现用户调用一次接口之后让接口次数增多的操作之后,又迎来了新的问题。

就是我们在调用接口的时候需要对用户进行校验,对调用的接口是否存在进行验证。

从这个需求出发,我们第一反应想到的解决办法是什么,应该是在api-gateway项目中也来引入一下这个数据库配置,包括三层架构重新写一轮,包括实体类也需要引入一下

这样做的问题其实很明显,就是代码很冗余,如果以后微服务的模块开发多了,那每个模块都需要引入这个数据库巴拉巴拉一堆配置,这就很麻烦,也不易于维护

所以从实际需求出发,我们就需要用到这个微服务中的远程调用的操作(我感觉对于微服务来说这其实应该是一个很基础的操作,但是我写这个项目的时候,微服务还没学到远程调用)

首先我们可以先来简单想一下这个我们如果想在其它项目中使用其它项目的方法,我们可以怎么做

  1. 我们可以用笨办法,直接cv,然后搭环境配依赖
  2. HTTP 请求(提供一个接口,供其他项目调用)(这个方式我没有用过,看鱼皮的笔记先进行一个记录)
  3. 第三个就是RPC
  4. 第四个其实我们也肯定知道,就是打个jar包,这也有点类似于自定义start的方式

根据项目背景我们就可以想到用RPC和Dubbo框架来实现远程调用的方式了。


简单的概念介绍:

什么是RPC?

作用:像调用本地方法一样调用远程方法。

和直接 HTTP 调用的区别:

  1. 对开发者更透明,减少了很多的沟通成本。
  2. RPC 向远程服务器发送请求时,未必要使用 HTTP 协议,比如还可以用 TCP / IP,性能更高。(内部服务更适用)

什么理解这个像调用本地方法一样调用远程方法,就是比如我们在我们的项目中的三层架构,controller直接调用mapper那样方便。

远程调用的流程:

为什么可以远程调用呢,

首先我们明白调用,肯定是有一方提供方法,一方调用你提供的方法

这里就会有三个角色,提供者调用方和注册中心

提供者根据地址先将方法注册到注册中心,

注册中心进行一个存储,

然后调用方也根据地址去注册中心里面去取你注册到里面的方法

这里的注册中心我用的是后面会记录的nacos。

什么是Dubbo框架:

这个是官方的基于springboot的案例3 - 基于 Spring Boot Starter 开发微服务应用 | Apache Dubbo

这里也有点坑,官方的这个案例里面用的是nacos,如果你没有下载和开启nacos啊,直接报错

我搞这个搞了半天才发现要下载nacos 

Dubbo 是一个高性能的Java RPC(远程过程调用)框架,最初由阿里巴巴开发并开源。它主要用于构建分布式服务,可以让服务提供者和消费者之间进行高效、安全的通信。

我们简单理解为就是封装了一些方法让我们可以非常简单的使用这个RPC进行远程调用。

 nacos注册中心:

nacos注册中心得单击非持久化搭建:

  • 单机:指的是 Nacos 运行在单个实例上,通常用于开发和测试环境。
  • 非持久化:表示注册的信息(如服务实例、元数据等)不会被保存在数据库中。Nacos 运行时的数据是在内存中,重启或崩溃后,所有的注册信息将会丢失。

     优点:

  • 简单易用,启动速度快,适合开发测试用。
  • 无需额外配置数据库。

    缺点:

  • 数据不持久化,产品环境不适用,无法保证服务注册信息的持久性和可靠性。

Nacos 快速开始 | Nacos 官网

官网上有详细步骤:

1:详细上来说就是下载解压压缩包

2:注册服务并用startup.cmd -m standalone命令开启,注意这里开发需要到nacos的bin目录下打开cmd输入startup.cmd -m standalone开启

 3:第三步我们就可以照着这上面的网址进行访问了

进入之后,如果我们有注册方法的话,这里就会有提示。

4:根据Dubbo框架的官方文档来使用nacos注册中心:

Nacos | Apache Dubbo

总体说起来就是、

引入对应依赖(包括dubbo的依赖和nacos的依赖)

        <dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo</artifactId><version>3.0.9</version></dependency><dependency><groupId>com.alibaba.nacos</groupId><artifactId>nacos-client</artifactId><version>2.1.0</version></dependency>

在配置文件中配置

# 以下配置指定了应用的名称、使用的协议(Dubbo)、注册中心的类型(Nacos)和地址
dubbo:application:# 设置应用的名称name: dubbo-springboot-demo-provider# 指定使用 Dubbo 协议,且端口设置为 -1,表示随机分配可用端口protocol:name: dubboport: -1registry:# 配置注册中心为 Nacos,使用的地址是 nacos://localhost:8848id: nacos-registryaddress: nacos://localhost:8848

这些东西直接复制官网就行。

注意点:

这些配置和依赖在provider引入了,在consumer也需要引入,并且需要保持一致

上面是在本地部署,本地部署相对简单安装一个压缩包,输入命令即可。


nacos注册中心的持久化到 MySQL 模式:

  • 持久化:表示 Nacos 运行时的服务注册信息会保存在 MySQL 数据库中。这意味着即使 Nacos 实例重启或发生故障,注册的信息仍然可以恢复,确保服务的可用性和数据的一致性。

优点

  • 数据持久化,增强了系统的可靠性,适合生产环境。
  • 支持高可用部署,可以通过多个 Nacos 实例搭建集群。

缺点

  • 需要配置和维护 MySQL 数据库,增加了部署成本和复杂性。
  • 启动和初次配置可能会稍显复杂。

我使用得是docker容器得搭建方式

1:需要配置mysql的脚本
-- --------------------------------------------------------
-- 主机:                           192.168.150.101
-- 服务器版本:                        8.0.27 - MySQL Community Server - GPL
-- 服务器操作系统:                      Linux
-- HeidiSQL 版本:                  12.2.0.6576
-- --------------------------------------------------------/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;-- 导出 nacos 的数据库结构
DROP DATABASE IF EXISTS `nacos`;
CREATE DATABASE IF NOT EXISTS `nacos` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;
USE `nacos`;-- 导出  表 nacos.config_info 结构
DROP TABLE IF EXISTS `config_info`;
CREATE TABLE IF NOT EXISTS `config_info` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',`data_id` varchar(255) COLLATE utf8_bin NOT NULL COMMENT 'data_id',`group_id` varchar(128) COLLATE utf8_bin DEFAULT NULL,`content` longtext COLLATE utf8_bin NOT NULL COMMENT 'content',`md5` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT 'md5',`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',`src_user` text COLLATE utf8_bin COMMENT 'source user',`src_ip` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT 'source ip',`app_name` varchar(128) COLLATE utf8_bin DEFAULT NULL,`tenant_id` varchar(128) COLLATE utf8_bin DEFAULT '' COMMENT '租户字段',`c_desc` varchar(256) COLLATE utf8_bin DEFAULT NULL,`c_use` varchar(64) COLLATE utf8_bin DEFAULT NULL,`effect` varchar(64) COLLATE utf8_bin DEFAULT NULL,`type` varchar(64) COLLATE utf8_bin DEFAULT NULL,`c_schema` text COLLATE utf8_bin,`encrypted_data_key` text COLLATE utf8_bin NOT NULL COMMENT '秘钥',PRIMARY KEY (`id`),UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='config_info';-- 正在导出表  nacos.config_info 的数据:~0 rows (大约)
DELETE FROM `config_info`;-- 导出  表 nacos.config_info_aggr 结构
DROP TABLE IF EXISTS `config_info_aggr`;
CREATE TABLE IF NOT EXISTS `config_info_aggr` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',`data_id` varchar(255) COLLATE utf8_bin NOT NULL COMMENT 'data_id',`group_id` varchar(128) COLLATE utf8_bin NOT NULL COMMENT 'group_id',`datum_id` varchar(255) COLLATE utf8_bin NOT NULL COMMENT 'datum_id',`content` longtext COLLATE utf8_bin NOT NULL COMMENT '内容',`gmt_modified` datetime NOT NULL COMMENT '修改时间',`app_name` varchar(128) COLLATE utf8_bin DEFAULT NULL,`tenant_id` varchar(128) COLLATE utf8_bin DEFAULT '' COMMENT '租户字段',PRIMARY KEY (`id`),UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='增加租户字段';-- 正在导出表  nacos.config_info_aggr 的数据:~0 rows (大约)
DELETE FROM `config_info_aggr`;-- 导出  表 nacos.config_info_beta 结构
DROP TABLE IF EXISTS `config_info_beta`;
CREATE TABLE IF NOT EXISTS `config_info_beta` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',`data_id` varchar(255) COLLATE utf8_bin NOT NULL COMMENT 'data_id',`group_id` varchar(128) COLLATE utf8_bin NOT NULL COMMENT 'group_id',`app_name` varchar(128) COLLATE utf8_bin DEFAULT NULL COMMENT 'app_name',`content` longtext COLLATE utf8_bin NOT NULL COMMENT 'content',`beta_ips` varchar(1024) COLLATE utf8_bin DEFAULT NULL COMMENT 'betaIps',`md5` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT 'md5',`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',`src_user` text COLLATE utf8_bin COMMENT 'source user',`src_ip` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT 'source ip',`tenant_id` varchar(128) COLLATE utf8_bin DEFAULT '' COMMENT '租户字段',`encrypted_data_key` text COLLATE utf8_bin NOT NULL COMMENT '秘钥',PRIMARY KEY (`id`),UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='config_info_beta';-- 正在导出表  nacos.config_info_beta 的数据:~0 rows (大约)
DELETE FROM `config_info_beta`;-- 导出  表 nacos.config_info_tag 结构
DROP TABLE IF EXISTS `config_info_tag`;
CREATE TABLE IF NOT EXISTS `config_info_tag` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',`data_id` varchar(255) COLLATE utf8_bin NOT NULL COMMENT 'data_id',`group_id` varchar(128) COLLATE utf8_bin NOT NULL COMMENT 'group_id',`tenant_id` varchar(128) COLLATE utf8_bin DEFAULT '' COMMENT 'tenant_id',`tag_id` varchar(128) COLLATE utf8_bin NOT NULL COMMENT 'tag_id',`app_name` varchar(128) COLLATE utf8_bin DEFAULT NULL COMMENT 'app_name',`content` longtext COLLATE utf8_bin NOT NULL COMMENT 'content',`md5` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT 'md5',`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',`src_user` text COLLATE utf8_bin COMMENT 'source user',`src_ip` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT 'source ip',PRIMARY KEY (`id`),UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='config_info_tag';-- 正在导出表  nacos.config_info_tag 的数据:~0 rows (大约)
DELETE FROM `config_info_tag`;-- 导出  表 nacos.config_tags_relation 结构
DROP TABLE IF EXISTS `config_tags_relation`;
CREATE TABLE IF NOT EXISTS `config_tags_relation` (`id` bigint NOT NULL COMMENT 'id',`tag_name` varchar(128) COLLATE utf8_bin NOT NULL COMMENT 'tag_name',`tag_type` varchar(64) COLLATE utf8_bin DEFAULT NULL COMMENT 'tag_type',`data_id` varchar(255) COLLATE utf8_bin NOT NULL COMMENT 'data_id',`group_id` varchar(128) COLLATE utf8_bin NOT NULL COMMENT 'group_id',`tenant_id` varchar(128) COLLATE utf8_bin DEFAULT '' COMMENT 'tenant_id',`nid` bigint NOT NULL AUTO_INCREMENT,PRIMARY KEY (`nid`),UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='config_tag_relation';-- 正在导出表  nacos.config_tags_relation 的数据:~0 rows (大约)
DELETE FROM `config_tags_relation`;-- 导出  表 nacos.group_capacity 结构
DROP TABLE IF EXISTS `group_capacity`;
CREATE TABLE IF NOT EXISTS `group_capacity` (`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',`group_id` varchar(128) COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群',`quota` int unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',`usage` int unsigned NOT NULL DEFAULT '0' COMMENT '使用量',`max_size` int unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',`max_aggr_count` int unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值',`max_aggr_size` int unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',`max_history_count` int unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',PRIMARY KEY (`id`),UNIQUE KEY `uk_group_id` (`group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表';-- 正在导出表  nacos.group_capacity 的数据:~0 rows (大约)
DELETE FROM `group_capacity`;-- 导出  表 nacos.his_config_info 结构
DROP TABLE IF EXISTS `his_config_info`;
CREATE TABLE IF NOT EXISTS `his_config_info` (`id` bigint unsigned NOT NULL,`nid` bigint unsigned NOT NULL AUTO_INCREMENT,`data_id` varchar(255) COLLATE utf8_bin NOT NULL,`group_id` varchar(128) COLLATE utf8_bin NOT NULL,`app_name` varchar(128) COLLATE utf8_bin DEFAULT NULL COMMENT 'app_name',`content` longtext COLLATE utf8_bin NOT NULL,`md5` varchar(32) COLLATE utf8_bin DEFAULT NULL,`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,`src_user` text COLLATE utf8_bin,`src_ip` varchar(50) COLLATE utf8_bin DEFAULT NULL,`op_type` char(10) COLLATE utf8_bin DEFAULT NULL,`tenant_id` varchar(128) COLLATE utf8_bin DEFAULT '' COMMENT '租户字段',`encrypted_data_key` text COLLATE utf8_bin NOT NULL COMMENT '秘钥',PRIMARY KEY (`nid`),KEY `idx_gmt_create` (`gmt_create`),KEY `idx_gmt_modified` (`gmt_modified`),KEY `idx_did` (`data_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='多租户改造';-- 正在导出表  nacos.his_config_info 的数据:~0 rows (大约)
DELETE FROM `his_config_info`;-- 导出  表 nacos.permissions 结构
DROP TABLE IF EXISTS `permissions`;
CREATE TABLE IF NOT EXISTS `permissions` (`role` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,`resource` varchar(255) COLLATE utf8mb4_general_ci NOT NULL,`action` varchar(8) COLLATE utf8mb4_general_ci NOT NULL,UNIQUE KEY `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;-- 正在导出表  nacos.permissions 的数据:~0 rows (大约)
DELETE FROM `permissions`;-- 导出  表 nacos.roles 结构
DROP TABLE IF EXISTS `roles`;
CREATE TABLE IF NOT EXISTS `roles` (`username` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,`role` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,UNIQUE KEY `idx_user_role` (`username`,`role`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;-- 正在导出表  nacos.roles 的数据:~1 rows (大约)
DELETE FROM `roles`;
INSERT INTO `roles` (`username`, `role`) VALUES('nacos', 'ROLE_ADMIN');-- 导出  表 nacos.tenant_capacity 结构
DROP TABLE IF EXISTS `tenant_capacity`;
CREATE TABLE IF NOT EXISTS `tenant_capacity` (`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',`tenant_id` varchar(128) COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT 'Tenant ID',`quota` int unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',`usage` int unsigned NOT NULL DEFAULT '0' COMMENT '使用量',`max_size` int unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',`max_aggr_count` int unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数',`max_aggr_size` int unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',`max_history_count` int unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',PRIMARY KEY (`id`),UNIQUE KEY `uk_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='租户容量信息表';-- 正在导出表  nacos.tenant_capacity 的数据:~0 rows (大约)
DELETE FROM `tenant_capacity`;-- 导出  表 nacos.tenant_info 结构
DROP TABLE IF EXISTS `tenant_info`;
CREATE TABLE IF NOT EXISTS `tenant_info` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',`kp` varchar(128) COLLATE utf8_bin NOT NULL COMMENT 'kp',`tenant_id` varchar(128) COLLATE utf8_bin DEFAULT '' COMMENT 'tenant_id',`tenant_name` varchar(128) COLLATE utf8_bin DEFAULT '' COMMENT 'tenant_name',`tenant_desc` varchar(256) COLLATE utf8_bin DEFAULT NULL COMMENT 'tenant_desc',`create_source` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT 'create_source',`gmt_create` bigint NOT NULL COMMENT '创建时间',`gmt_modified` bigint NOT NULL COMMENT '修改时间',PRIMARY KEY (`id`),UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`),KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='tenant_info';-- 正在导出表  nacos.tenant_info 的数据:~0 rows (大约)
DELETE FROM `tenant_info`;-- 导出  表 nacos.users 结构
DROP TABLE IF EXISTS `users`;
CREATE TABLE IF NOT EXISTS `users` (`username` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,`password` varchar(500) COLLATE utf8mb4_general_ci NOT NULL,`enabled` tinyint(1) NOT NULL,PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;-- 正在导出表  nacos.users 的数据:~1 rows (大约)
DELETE FROM `users`;
INSERT INTO `users` (`username`, `password`, `enabled`) VALUES('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', 1);/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;

这个直接在服务器配置好的mysql中执行即可。

2:创建nacos和mysql的配置文件:

可以新建一个文件夹nacos(这步是可选的,我是为了以后我方便找)

配置文件:

PREFER_HOST_MODE=hostname
MODE=standalone
SPRING_DATASOURCE_PLATFORM=mysql
MYSQL_SERVICE_HOST=自己的服务器ip地址
MYSQL_SERVICE_DB_NAME=nacos
MYSQL_SERVICE_PORT=3307//自己mysql的端口
MYSQL_SERVICE_USER=root
MYSQL_SERVICE_PASSWORD=123
MYSQL_SERVICE_DB_PARAM=characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai

springdatasourceplatform就是指定了数据库的类型,说明nacos可以配置很多不同的数据库

接着往下就是自己的基本信息。

3:拉取镜像:

我这里拉取的是nacos/nacos-server:v2.1.0-slim

docker pull nacos/nacos-server:v2.1.0-slim

执行命令即可 

4:在对应文件目录中创建容器并运行,并访问登录页

这个对应文件目录就是你nacos存放的目录

直接在客户端内cd 到当前目录

并且执行:

docker run -d \
--name nacos \
--env-file ./nacos/custom.env \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--restart=always \
nacos/nacos-server:v2.1.0-slim

注意第三行的 ./nacos/custom.env \

如果你上面没有新建文件夹就修改一下这个路径。

 启动完成后

我们也可以docker ps看一下

下一步需要注意

就是我用的是腾讯云的服务器

如果你装的是虚拟机那可以不用

如果是服务器,还要到外层去开一下对应的端口

需要开的端口有三个:

8848:这是 Nacos 的默认 HTTP 端口,用于提供 Nacos 的 Web 界面和服务 API

9848:这个端口是 Nacos 的 若干功能服务的卸载装载

9849:这个端口通常用于 Nacos 的 gRPC 相关服务

要不然服务器无法访问登录页

开放了端口之后就可以访问登录页了

账号和密码初始化都是nacos:

这样就配置好了


讲完了一些基础概念,下面上一个Demo演示流程

先用Demo会更好入门会更好一点,我刚刚自己重新看了一遍流程,我自己在项目中将需要远程调用的方法抽象成了一个公共模块,所以,感觉理解起来会有点难。

实战:

Demo案例:

在实战之前我们需要引入上面的依赖

并且在springboot的启动类上加上

@EnableDubbo
@SpringBootApplication
@MapperScan("com.yupi.project.mapper")
@EnableDubbo
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);}}

provider和consumer的启动类都要加

首先我们从远程调用的过程入手。

我们首先需要一个provider来提供方法

这里的提供者是master这个项目

我们在这个项目中新建了一个软件包叫provider,里面很简单就是一个接口来规定调用的方法

一个实现这个接口的实现类来重写里面的方法。

import java.util.concurrent.CompletableFuture;public interface DemoService {String sayHello(String name);String sayHello2(String name);default CompletableFuture<String> sayHelloAsync(String name) {return CompletableFuture.completedFuture(sayHello(name));}}
package com.yupi.project.provider;import org.apache.dubbo.config.annotation.DubboService;
import org.apache.dubbo.rpc.RpcContext;@DubboService
public class DemoServiceImpl implements DemoService {@Overridepublic String sayHello(String name) {System.out.println("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());return "Hello " + name;}@Overridepublic String sayHello2(String name) {return "ljh";}}

很简单两个方法:输出hello和一个字符串ljh


根据流程下一步得到注册中心了:

看一下注册中心里面的信息:


下一步就是consumer了 

consumer就是这里的gateway项目:

首先我同样创建了一个名字一样的软件包,里面也有一个DemoService来接收这个提供者提供的方法。

package com.yupi.project.provider;import java.util.concurrent.CompletableFuture;public interface DemoService {String sayHello(String name);String sayHello2(String name);default CompletableFuture<String> sayHelloAsync(String name) {return CompletableFuture.completedFuture(sayHello(name));}}

没有看错和上面的provider就是一模一样的。

调用该接口中的方法实在启动类中

这里放在启动类没有什么特殊含义,单纯就是懒得再开一个软件包而已

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,DataSourceTransactionManagerAutoConfiguration.class,HibernateJpaAutoConfiguration.class})
@EnableDubbo
@Service
public class ApiSpringcloudgatewayApplication {@DubboReferenceprivate DemoService demoService;public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(ApiSpringcloudgatewayApplication.class, args);ApiSpringcloudgatewayApplication application = context.getBean(ApiSpringcloudgatewayApplication.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);}@Beanpublic GlobalFilter customFilter() {return new CustomGlobalFilter();}
}

我们从上到下分析,首先加上@EnableDubbo这个需要记得

    @DubboReferenceprivate DemoService demoService;

我觉得着有点像spring的依赖注入,但可能这个是Dubbo框架提供的赋值的方式。

ConfigurableApplicationContext context = SpringApplication.run(ApiSpringcloudgatewayApplication.class, args);ApiSpringcloudgatewayApplication application = context.getBean(ApiSpringcloudgatewayApplication.class);

这两行就是获取spring的bean工厂,之前在研究spring的源码的时候,就有了解过,bean工厂

然后获取了bean工厂之后再从工厂里面获取对应的bean对象。

接着就是调用provider提供的方法了。

输出结果:

接下来就是项目实战了,这个项目背景就是API开发平台的项目。

项目实战:

首先我在上面介绍的时候有提到,我对这个公共模块进行了抽取。

为什么会想到这个呢

我们仔细看上面的Demo案例,

我们的DemoService是不是在provider和consumer都写了一次。

我们基于这个操作,我们是不是就可以想到我们可以将这个模块抽取出来

再者,抽取公共模块也可以更好的就是规范我们的业务流程。

所以在项目实战之前

我们先抽取一下公共模块

抽取公共模块并在provider中提供接口:
抽象公共模块的步骤

1:想清楚什么方法需要抽象,或者说什么方法是公共,还有其它模块是需要使用的
2:想清楚实体类规整到公共模块还是原来的单体架构模块
3:打包引入依赖,引入依赖之后,需要照着公共的接口创建实现改接口的实体类

根据上面的步骤(其实这个步骤并不是什么规范步骤,就是我自己在抽取的时候遇到了问题,后面复盘总结起来的步骤)

先说一下,一般微服务抽取公共项目的名称都叫什么comon,所以我这里也取名叫api-common

想一下,我们需要抽取什么?
第一个就是我们在项目中需要其它项目引用的方法:

那得结合一下我们的业务

我们在网关的过滤器中,我们需要

1:对用户进行API签名的认证,这个的具体步骤是根据用户在请求头中的accessKey从数据库中查出用户的secretKey,然后结合用户的id+secretKey计算sign,和请求头中发过来的sign进行比对校验。具体更详细步骤在API网关认证哪一章有具体讲

2:查询接口是否存在,这个就是根据接口id到数据库中进行查询

3:调用次数+1,这个就是到UserInterfaceInfo中讲totalNum+1,讲LeftNum-1即可

分析了业务之后,发现我们的需要的方法:


/*** 内部接口信息服务**/
public interface InnerInterfaceInfoService {/*** 从数据库中查询模拟接口是否存在(请求路径、请求方法、请求参数)*/InterfaceInfo getInterfaceInfo(String path, String method);
}
/*** 内部用户接口信息服务***/
public interface InnerUserInterfaceInfoService {/*** 调用接口统计* @param interfaceInfoId* @param userId* @return*/boolean invokeCount(long interfaceInfoId, long userId);
}
import com.yupi.model.entity.User;/*** 内部用户服务**/
public interface InnerUserService {/*** 数据库中查是否已分配给用户秘钥(accessKey)* @param accessKey* @return*/User getInvokeUser(String accessKey);
}
第二个就是实体类

第二个就是上面这三个接口中要用到的实体类

想清楚实体类规整到公共模块还是原来的单体架构模块

这个点就是我在抽取的时候没搞清楚的地方了,就是我有的实体类在我master项目中,

有的在common公共模块中

我后面就索性全部改到common公共模块中

并且将master项目中的mapper和mapper,xml都进行了修改

打包引入依赖,引入依赖之后,需要照着公共的接口创建实现改接口的实体类

这里的打包引入依赖就和自定义starter一样。

照着公共的接口创建实现改接口的实体类:

这里当然也可以调用原来的service的方法

不过为了规范一点可以新建一个inner软件包

@DubboService//@DubboService注解是加在你想要暴露出去的方法上的
public class InnerInterfaceInfoServiceImpl implements InnerInterfaceInfoService {@Resourceprivate InterfaceInfoMapper interfaceInfoMapper;@Overridepublic InterfaceInfo getInterfaceInfo(String url, String method) {if (StringUtils.isAnyBlank(url, method)) {throw new BusinessException(ErrorCode.PARAMS_ERROR);}LambdaQueryWrapper<InterfaceInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();LambdaQueryWrapper<InterfaceInfo> eq = lambdaQueryWrapper.eq(InterfaceInfo::getUrl, url).eq(InterfaceInfo::getMethod, method);InterfaceInfo interfaceInfo = interfaceInfoMapper.selectOne(eq);return interfaceInfo;}}
/*** 内部用户接口信息服务实现类**/
@DubboService
public class InnerUserInterfaceInfoServiceImpl implements InnerUserInterfaceInfoService {@Resourceprivate UserInterfaceInfoService userInterfaceInfoService;@Overridepublic boolean invokeCount(long interfaceInfoId, long userId) {return userInterfaceInfoService.invokecount(interfaceInfoId,userId);}
}
/*** 内部用户服务实现类**/
@DubboService
public class InnerUserServiceImpl implements InnerUserService {@Resourceprivate UserMapper userMapper;@Overridepublic User getInvokeUser(String accessKey) {if (StringUtils.isAnyBlank(accessKey)) {throw new BusinessException(ErrorCode.PARAMS_ERROR);}QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.eq("accessKey", accessKey);return userMapper.selectOne(queryWrapper);}
}

注意要在这些类上加上 @DubboService注解,注册到nacos注册中心中


等抽取完公共模块之后,剩下的步骤就很简单了

毕竟RPC的宣称就是像调用本地方法一样调用远程方法

 consumer调用远程接口:
@Slf4j
public class CustomGlobalFilter implements GlobalFilter, Ordered {@DubboReferenceprivate InnerUserService innerUserService;@DubboReferenceprivate InnerInterfaceInfoService innerInterfaceInfoService;@DubboReferenceprivate InnerUserInterfaceInfoService innerUserInterfaceInfoService;private static ArrayList<String> IP_WHITE_LIST = new ArrayList<>();private static final String INTERFACE_HOST = "http://localhost:8123";@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//1:请求日志//2:访问控制 -黑白名单//3:用户鉴权final HttpHeaders headers = request.getHeaders();final String accessKey = headers.getFirst("accessKey");String random = headers.getFirst("random");String timestamp = headers.getFirst("timestamp");String sign = headers.getFirst("sign");String body = headers.getFirst("body");final User invokeUser = innerUserService.getInvokeUser(accessKey);if(invokeUser==null){//user==null,说明这个accessKey根本没有被分配给用户throw new RuntimeException("accessKey不存在");}if(Long.parseLong(random) > 10000){handleNoAuth(response);}//通过时间戳判断是否过期long currentTimeMillis = System.currentTimeMillis()/1000;long differenceInSeconds = (long) (currentTimeMillis/1000 - Long.parseLong(timestamp));if(differenceInSeconds > 300){handleNoAuth(response);}final String secretKey = invokeUser.getSecretKey();String flag = SingUtils.getSign(body, secretKey);if(!flag.equals(sign)){handleNoAuth(response);}final Long userId = invokeUser.getId();//4:从数据库中查询模拟接口是否存在System.out.println(path);System.out.println(method);InterfaceInfo interfaceInfo = innerInterfaceInfoService.getInterfaceInfo(path,method);if(interfaceInfo==null){handleNoAuth(response);}final Long interfaceInfoId = interfaceInfo.getId();//5:请求转化,调用模拟接口final Mono<Void> filter = chain.filter(exchange);//在这个方法中调用次数+1return handleResponse(exchange,chain,42L,userId);} 
}

源代码很多

流行需要调用远程方法的部分

在调用之前,我们需要先注入一下,这三个接口

就和之前的DemoService一样 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/396288.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】详解IPC:共享内存

目录 IPC 共享内存 1.理解 2.运用 1. 创建 ipc - shmget 2. 创建 key - ftok ⭕shmid vs key 3. 连接 - shmat 4. 脱离 - shmdt 5. 控制/删除 - shmctl 总结 代码示例 3.实验 comm.hpp processa.cc processb.cc log.hpp makefile 测试 4.思考 IPC 进程间通…

vue3引入模块报错:无法找到模块“xxx”的声明文件

使用vue3ts导入vue文件的时候&#xff0c;报错&#xff1a;找不到模块“./XXX.vue”或其相应的类型声明 这是由于&#xff1a;Vue 文件并不是标准的 JavaScript 模块&#xff0c;因此 TypeScript 需要通过这种声明方式来理解和处理这些文件 我是使用vite创建的项目&#xff0…

【生信入门linux篇】如何安装一个linux虚拟机用于学习

一.虚拟机 虚拟机&#xff08;Virtual Machine&#xff0c;简称VM&#xff09;是一种软件实现的计算机系统&#xff0c;它能够在物理计算机上模拟出多个独立的计算机环境。每个虚拟机都可以运行自己的操作系统和应用程序&#xff0c;就像在独立的物理计算机上一样。虚拟机技术…

小试牛刀-区块链Solana多签账户

目录 1.什么是多签账户 2.多签账户的特点 2.1 多个签名者 2.2 最小签名要求 2.3 常见应用场景 3.多签账户实现 3.1 账户的创建 3.1.1 创建新账户 3.1.2 获取创建和初始账户事务 3.1.3 账户的签名 3.2 代币转移操作 Welcome to Code Blocks blog 本篇文章主要介绍了 …

LeetCode_sql_day16(601.体育馆的人流量)

描述&#xff1a;601. 体育馆的人流量 - 力扣&#xff08;LeetCode&#xff09; 编写解决方案找出每行的人数大于或等于 100 且 id 连续的三行或更多行记录。 返回按 visit_date 升序排列 的结果表。 输入Stadium表: ----------------------------- | id | visit_date | peop…

电子电气架构 --- 车辆模式管理

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

CUDA-MODE 第一课: 如何在 PyTorch 中 profile CUDA kernels

我的课程笔记&#xff0c;欢迎关注&#xff1a;https://github.com/BBuf/how-to-optim-algorithm-in-cuda/tree/master/cuda-mode 第一课: 如何在 PyTorch 中 profile CUDA kernels 这里是课程规划&#xff0c;有三位讲师 Andreas, Thomas, Mark&#xff0c;然后大概2周出一个 …

Elasticsearch:用例、架构和 6 个最佳实践

1. 什么是 Elasticsearch&#xff1f; Elasticsearch 是一个开源分布式搜索和分析引擎&#xff0c;专为处理大量数据而设计。它建立在 Apache Lucene 之上&#xff0c;并由Elastic 支持。Elasticsearch 用于近乎实时地存储、搜索和分析结构化和非结构化数据。 Elasticsearch 的…

4.3.2 C++ 平面拟合的实现

4.3.2 C 平面拟合的实现 参考教程&#xff1a; gaoxiang12/slam_in_autonomous_driving: 《自动驾驶中的SLAM技术》对应开源代码 (github.com) Eigen打印输出_打印eigen矩阵-CSDN博客 1. 编写 Plane fitting 1.1 创建文件夹 通过终端创建一个名为Plane_fitting的文件夹以保…

文件操作与IO(下)

✨个人主页&#xff1a; 不漫游-CSDN博客 目录 前言 流对象 InputStream OutputStream 运用 在控制台进行输入并写入文件 进行普通文件的复制 前言 之前的文章文件操作与IO&#xff08;上&#xff09;已经介绍了文件系统的相关操作&#xff0c;这次的主角是文件内容的相关…

SpringBoot 框架学习笔记(七):Thymeleaf、拦截器 和 文件上传实现(解决了文件重名 和 按日期分目录存放问题)

1 Thymeleaf 1.1 基本介绍 &#xff08;1&#xff09;官方文档&#xff1a;Tutorial: Using Thymeleaf &#xff08;2&#xff09;Thymeleaf 是什么 Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎&#xff0c;可完全替代 JSPThymeleaf 是一个 java 类库&#xf…

.net core webapi 自定义异常过滤器

1.定义统一返回格式 namespace webapi;/// <summary> /// 统一数据响应格式 /// </summary> public class Results<T> {/// <summary>/// 自定义的响应码&#xff0c;可以和http响应码一致&#xff0c;也可以不一致/// </summary>public int Co…

vue 打包时候的分包

export default defineConfig({plugins: [vue()],resolve: {alias: {: fileURLToPath(new URL(./src/, import.meta.url))}},// 分包&#xff0c;node_modules中的单独打包成名字为vendor的js文件build: {rollupOptions: {manualChunks(id) {if (id.includes(node_modules)) {r…

EF8 学习过程中的问题和解决方案

一、varchar类型字段如果为null 无法使用contains来判断是否包含字符串 1. 有问题的代码&#xff1a; contractList _dbcontext.contractHeads.Where(u > u.code.Contains(queryStr) || u.name.Contains(queryStr) || u.companyName.Contains(queryStr) || u.customerNa…

uniapp开启数据压缩的坑-SpringBoot-gzip

1、服务器配置 服务端开启的数据压缩配置 server:port: ${port:8881}servlet:# 应用上下文路径context-path: /orderserverundertow:threads:io: 4worker: 500buffer-size: 2048# 开启Gzip压缩&#xff0c;compression:# 开启压缩enabled: true# 对json格式内容进行压缩mime-…

KCTF 闯关游戏:1 ~ 7 关

前言 看雪CTF平台是一个专注于网络安全技术竞赛的在线平台&#xff0c;它提供了一个供网络安全爱好者和技术专家进行技术交流、学习和竞技的环境。CTF&#xff08;Capture The Flag&#xff0c;夺旗赛&#xff09;是网络安全领域内的一种流行竞赛形式&#xff0c;起源于1996年…

嵌入式全栈开发学习笔记---数据结构(排序算法)

目录 排序的分类 稳定排序与不稳定排序 内部排序和外部排序 算法的复杂性 常见的排序算法 直接插入排序 希尔排序 快速排序 简单选择排序 堆排序 归并排序 基数排序 常见的排序总结 到目前为止&#xff0c;数据结构的线性结构和树状结构就都讲完了&#xff0c;本节…

使用 MongoDB 构建 AI:Flagler Health 的 AI 旅程如何彻底改变患者护理

Flagler Health 致力于为慢性病患者提供支持&#xff0c;为其匹配合适的医生以提供合适的护理。 通常&#xff0c;身患严重病痛的患者面临的选择有限&#xff0c;他们往往需要长期服用阿片类药物&#xff0c;或寻求成本高昂的侵入性外科手术干预。遗憾的是&#xff0c;后一种方…

SQL语句创建数据库(增删查改)

SQL语句 一.数据库的基础1.1 什么是数据库1.2 基本使用1.2.1 连接服务器1.2.2 使用案例 1.2 SQL分类 二.库的操作2.1 创建数据库2.2 创建数据库示例2.3 字符集和校验规则2.3.1 查看系统默认字符集以及校验规则2.3.2查看数据库支持的字符集2.3.3查看数据库支持的字符集校验规则2…

Android系统Android.bp文件详解

文章目录 1. 基本语法结构2. 常见模块类型3. 模块属性常见属性包括&#xff1a; 4. 具体示例5. 高级功能5.1. 条件编译5.2. 变量定义与使用5.3. 模块继承 6. 总结 Android.bp 是 Android 构建系统&#xff08;Android Build System&#xff09;中的配置文件&#xff0c;用于描述…