【第31章】Spring Cloud之Sentinel控制台推送规则到Nacos

news2024/12/24 20:22:04

文章目录

  • 前言
  • 一、下载源码
    • 1. 下载源码
  • 二、规则配置
    • 1. Nacos适配
      • 1.1 使用数据源
      • 1.2 复制官方案例
      • 1.3 动态规则配置中心
    • 2. 前端路由配置
    • 3. 提示
    • 4. 编译和启动
  • 三、测试
    • 1. 修改前
    • 2. 修改后
  • 总结


前言

前面我们已经完成了通过nacos存储提供者流控配置文件,下面我们来接入Sentinel控制台推送规则到Nacos数据源。


一、下载源码

这里我们需要修改sentinel-dashboard源码,并重新编译打包,后续启动使用新包。

1. 下载源码

下载地址
在这里插入图片描述

二、规则配置

要通过 Sentinel 控制台配置集群流控规则,需要对控制台进行改造。我们提供了相应的接口进行适配。

从 Sentinel 1.4.0 开始,我们抽取出了接口用于向远程配置中心推送规则以及拉取规则:

  • DynamicRuleProvider: 拉取规则
  • DynamicRulePublisher: 推送规则

对于集群限流的场景,由于每个集群限流规则都需要唯一的 flowId,因此我们建议所有的规则配置都通过动态规则源进行管理,并在统一的地方生成集群限流规则。

我们提供了新版的流控规则页面,可以针对应用维度推送规则,对于集群限流规则可以自动生成 flowId。用户只需实现 DynamicRuleProvider 和 DynamicRulePublisher 接口,即可实现应用维度推送(URL: /v2/flow)。

1. Nacos适配

我们提供了 Nacos、ZooKeeper 和 Apollo 的推送和拉取规则实现示例(位于 test 目录下)。以 Nacos 为例,若希望使用 Nacos 作为动态规则配置中心,用户可以提取出相关的类,然后只需在 FlowControllerV2 中指定对应的 bean 即可开启 Nacos 适配。

1.1 使用数据源

        <!-- for Nacos rule publisher sample -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
<!--            <scope>test</scope>-->
        </dependency>

1.2 复制官方案例

1.3 动态规则配置中心

使用Nacos推送和拉取规则

FlowControllerV2

/*
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.csp.sentinel.dashboard.controller.v2;

import java.util.Date;
import java.util.List;

import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.util.StringUtil;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemoryRuleRepositoryAdapter;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.dashboard.domain.Result;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * Flow rule controller (v2).
 *
 * @author Eric Zhao
 * @since 1.4.0
 */
@RestController
@RequestMapping(value = "/v2/flow")
public class FlowControllerV2 {

    private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class);

    @Autowired
    private InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;

//    @Autowired
//    @Qualifier("flowRuleDefaultProvider")
//    private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
//    @Autowired
//    @Qualifier("flowRuleDefaultPublisher")
//    private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;

    @Autowired
    @Qualifier("flowRuleNacosProvider")
    private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
    @Autowired
    @Qualifier("flowRuleNacosPublisher")
    private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;

    @GetMapping("/rules")
    @AuthAction(PrivilegeType.READ_RULE)
    public Result<List<FlowRuleEntity>> apiQueryMachineRules(@RequestParam String app) {

        if (StringUtil.isEmpty(app)) {
            return Result.ofFail(-1, "app can't be null or empty");
        }
        try {
            List<FlowRuleEntity> rules = ruleProvider.getRules(app);
            if (rules != null && !rules.isEmpty()) {
                for (FlowRuleEntity entity : rules) {
                    entity.setApp(app);
                    if (entity.getClusterConfig() != null && entity.getClusterConfig().getFlowId() != null) {
                        entity.setId(entity.getClusterConfig().getFlowId());
                    }
                }
            }
            rules = repository.saveAll(rules);
            return Result.ofSuccess(rules);
        } catch (Throwable throwable) {
            logger.error("Error when querying flow rules", throwable);
            return Result.ofThrowable(-1, throwable);
        }
    }

    private <R> Result<R> checkEntityInternal(FlowRuleEntity entity) {
        if (entity == null) {
            return Result.ofFail(-1, "invalid body");
        }
        if (StringUtil.isBlank(entity.getApp())) {
            return Result.ofFail(-1, "app can't be null or empty");
        }
        if (StringUtil.isBlank(entity.getLimitApp())) {
            return Result.ofFail(-1, "limitApp can't be null or empty");
        }
        if (StringUtil.isBlank(entity.getResource())) {
            return Result.ofFail(-1, "resource can't be null or empty");
        }
        if (entity.getGrade() == null) {
            return Result.ofFail(-1, "grade can't be null");
        }
        if (entity.getGrade() != 0 && entity.getGrade() != 1) {
            return Result.ofFail(-1, "grade must be 0 or 1, but " + entity.getGrade() + " got");
        }
        if (entity.getCount() == null || entity.getCount() < 0) {
            return Result.ofFail(-1, "count should be at lease zero");
        }
        if (entity.getStrategy() == null) {
            return Result.ofFail(-1, "strategy can't be null");
        }
        if (entity.getStrategy() != 0 && StringUtil.isBlank(entity.getRefResource())) {
            return Result.ofFail(-1, "refResource can't be null or empty when strategy!=0");
        }
        if (entity.getControlBehavior() == null) {
            return Result.ofFail(-1, "controlBehavior can't be null");
        }
        int controlBehavior = entity.getControlBehavior();
        if (controlBehavior == 1 && entity.getWarmUpPeriodSec() == null) {
            return Result.ofFail(-1, "warmUpPeriodSec can't be null when controlBehavior==1");
        }
        if (controlBehavior == 2 && entity.getMaxQueueingTimeMs() == null) {
            return Result.ofFail(-1, "maxQueueingTimeMs can't be null when controlBehavior==2");
        }
        if (entity.isClusterMode() && entity.getClusterConfig() == null) {
            return Result.ofFail(-1, "cluster config should be valid");
        }
        return null;
    }

    @PostMapping("/rule")
    @AuthAction(value = AuthService.PrivilegeType.WRITE_RULE)
    public Result<FlowRuleEntity> apiAddFlowRule(@RequestBody FlowRuleEntity entity) {

        Result<FlowRuleEntity> checkResult = checkEntityInternal(entity);
        if (checkResult != null) {
            return checkResult;
        }
        entity.setId(null);
        Date date = new Date();
        entity.setGmtCreate(date);
        entity.setGmtModified(date);
        entity.setLimitApp(entity.getLimitApp().trim());
        entity.setResource(entity.getResource().trim());
        try {
            entity = repository.save(entity);
            publishRules(entity.getApp());
        } catch (Throwable throwable) {
            logger.error("Failed to add flow rule", throwable);
            return Result.ofThrowable(-1, throwable);
        }
        return Result.ofSuccess(entity);
    }

    @PutMapping("/rule/{id}")
    @AuthAction(AuthService.PrivilegeType.WRITE_RULE)

    public Result<FlowRuleEntity> apiUpdateFlowRule(@PathVariable("id") Long id,
                                                    @RequestBody FlowRuleEntity entity) {
        if (id == null || id <= 0) {
            return Result.ofFail(-1, "Invalid id");
        }
        FlowRuleEntity oldEntity = repository.findById(id);
        if (oldEntity == null) {
            return Result.ofFail(-1, "id " + id + " does not exist");
        }
        if (entity == null) {
            return Result.ofFail(-1, "invalid body");
        }

        entity.setApp(oldEntity.getApp());
        entity.setIp(oldEntity.getIp());
        entity.setPort(oldEntity.getPort());
        Result<FlowRuleEntity> checkResult = checkEntityInternal(entity);
        if (checkResult != null) {
            return checkResult;
        }

        entity.setId(id);
        Date date = new Date();
        entity.setGmtCreate(oldEntity.getGmtCreate());
        entity.setGmtModified(date);
        try {
            entity = repository.save(entity);
            if (entity == null) {
                return Result.ofFail(-1, "save entity fail");
            }
            publishRules(oldEntity.getApp());
        } catch (Throwable throwable) {
            logger.error("Failed to update flow rule", throwable);
            return Result.ofThrowable(-1, throwable);
        }
        return Result.ofSuccess(entity);
    }

    @DeleteMapping("/rule/{id}")
    @AuthAction(PrivilegeType.DELETE_RULE)
    public Result<Long> apiDeleteRule(@PathVariable("id") Long id) {
        if (id == null || id <= 0) {
            return Result.ofFail(-1, "Invalid id");
        }
        FlowRuleEntity oldEntity = repository.findById(id);
        if (oldEntity == null) {
            return Result.ofSuccess(null);
        }

        try {
            repository.delete(id);
            publishRules(oldEntity.getApp());
        } catch (Exception e) {
            return Result.ofFail(-1, e.getMessage());
        }
        return Result.ofSuccess(id);
    }

    private void publishRules(/*@NonNull*/ String app) throws Exception {
        List<FlowRuleEntity> rules = repository.findAllByApp(app);
        rulePublisher.publish(app, rules);
    }
}

NacosConfig

/*
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.csp.sentinel.dashboard.rule.nacos;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;
import java.util.Properties;

/**
 *定义nacos连接信息
 */
@Configuration
public class NacosConfig {

    @Bean
    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
        return s -> JSON.parseArray(s, FlowRuleEntity.class);
    }

    @Bean
    public ConfigService nacosConfigService() throws Exception {
        Properties properties = new Properties();
        properties.setProperty("serverAddr","localhost");
        properties.setProperty("namespace","dev");
        properties.setProperty("username","admin");
        properties.setProperty("password","admin");
        return ConfigFactory.createConfigService(properties);
    }
}

NacosConfigUtil

/*
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.csp.sentinel.dashboard.rule.nacos;

/**
 *主要是定义GROUP_ID和FLOW_DATA_ID_POSTFIX
 */
public final class NacosConfigUtil {

    public static final String GROUP_ID = "SENTINEL_GROUP";
    
    public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules.json";
    public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules";
    public static final String CLUSTER_MAP_DATA_ID_POSTFIX = "-cluster-map";

    /**
     * cc for `cluster-client`
     */
    public static final String CLIENT_CONFIG_DATA_ID_POSTFIX = "-cc-config";
    /**
     * cs for `cluster-server`
     */
    public static final String SERVER_TRANSPORT_CONFIG_DATA_ID_POSTFIX = "-cs-transport-config";
    public static final String SERVER_FLOW_CONFIG_DATA_ID_POSTFIX = "-cs-flow-config";
    public static final String SERVER_NAMESPACE_SET_DATA_ID_POSTFIX = "-cs-namespace-set";

    private NacosConfigUtil() {}
}

2. 前端路由配置

前端页面需要手动切换,或者修改前端路由配置(sidebar.html 流控规则路由从 dashboard.flowV1 改成 dashboard.flow 即可,注意簇点链路页面对话框需要自行改造)。
在这里插入图片描述

3. 提示

默认 Nacos 适配的 dataId 和 groupId 约定如下:

  • groupId: SENTINEL_GROUP
  • 流控规则 dataId: {appName}-flow-rules,比如应用名为 appA,则 dataId 为 appA-flow-rules

用户可以在 NacosConfigUtil 修改对应的 groupId 和 dataId postfix。用户可以在 NacosConfig 配置对应的 Converter,默认已提供 FlowRuleEntity 的 decoder 和 encoder。

注意:接入端也需要注册对应的动态规则源,参考 集群流控规则配置文档。

4. 编译和启动

mvn clean package

三、测试

1. 修改前

第一次懒加载请求会全部通过
在这里插入图片描述

在这里插入图片描述

2. 修改后

在这里插入图片描述
在这里插入图片描述

控制台推送过来的没有样式,先将就吧
在这里插入图片描述

请求阈值升为6,请求全部通过,修改生效,符合预期
在这里插入图片描述


总结

回到顶部

使用Sentinel控制台+应用程序+Nacos,优点很明显,可视化配置+动态修改+持久化存储;
使用控制台之后也有个缺点,簇点链路和流控规则不互通了,官方给的说法是注意簇点链路页面对话框需要自行改造,最简单的办法就是原来的路由也放开,一块用呗!没试,大家自己试吧~

其实应用程序+Nacos完全够用,Sentinel控制台完全可以作为临时控制和调整使用,具体使用还是以配置文件为主,控制台为辅。

我调整后的这一版已上传附件,欢迎大家下载使用!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

腾讯云升级多个云存储解决方案 以智能化存储助力企业增长

9月6日&#xff0c;在腾讯数字生态大会腾讯云储存专场上&#xff0c;腾讯云升级多个存储解决方案&#xff1a;Data Platform 数据平台解决方案重磅发布&#xff0c;数据加速器 GooseFS、数据处理平台数据万象、日志服务 CLS、高性能并行文件存储 CFS Turbo 等多产品全新升级&am…

Nuxt Kit 的使用指南:模块创建与管理

title: Nuxt Kit 的使用指南:模块创建与管理 date: 2024/9/11 updated: 2024/9/11 author: cmdragon excerpt: 摘要:本文是关于Nuxt Kit的使用指南,重点介绍了如何使用defineNuxtModule创建自定义模块及installModule函数以编程方式安装模块,以增强Nuxt 3应用的功能性、…

JD18年秋招笔试疯狂数列python解答

问题如下&#xff1a; 链接&#xff1a;疯狂序列_京东笔试题_牛客网 [编程题]疯狂序列 热度指数&#xff1a;149 时间限制&#xff1a;C/C 1秒&#xff0c;其他语言2秒 空间限制&#xff1a;C/C 32M&#xff0c;其他语言64M 东东从京京那里了解到有一个无限长的数字序列: 1…

空间物联网中的大规模接入:挑战、机遇和未来方向

这篇论文的标题是《Massive Access in Space-based Internet of Things: Challenges, Opportunities, and Future Directions》&#xff0c;作者包括Jian Jiao, Shaohua Wu, Rongxing Lu, 和 Qinyu Zhang。文章发表在2021年10月的IEEE Wireless Communications上。论文主要探讨…

计算机毕业设计 半成品配菜平台的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

数据结构算法——排序算法

1.排序 1.选择排序 不稳定&#xff0c;一般不用&#xff0c;基本排序 思路&#xff1a;过滤数组&#xff0c;找到最小数&#xff0c;放在前面。 不稳&#xff1a;导致原本在前的数据移动到后面。 int arr[];for(i0;i<arr.length-1;i){int smallesti; for(ji1;j<leng…

计算机毕业设计 网上体育商城系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

加密

一、加密 加密运算需要两个输入&#xff1a;密钥和明文 解密运算也需要两个输入&#xff1a;密钥和密文 密文通常看起来都是晦涩难懂、毫无逻辑的&#xff0c;所以我们一般会通过传输或者存储密文来保护私密数据&#xff0c;当然&#xff0c;这建立在一个基础上&#xff0c;…

局域网windows下使用Git

windows下如何使用局域网进行git部署 准备工作第一步 &#xff0c;ip设置设置远程电脑的ip设置&#xff0c;如果不会设置请点击[这里](https://blog.csdn.net/Black_Friend/article/details/142170705?spm1001.2014.3001.5501)设置本地电脑的ip&#xff1a;验证 第二步&#x…

腾讯云使用

注&#xff1a;本文的所有演示的代码都基于尚硅谷的尚乐代驾项目 对象存储COS 一种云存储器 官方文档&#xff1a; 对象存储 快速入门-SDK 文档-文档中心-腾讯云 (tencent.com) 一 上传文件 1 初始化客户端 官方示例&#xff1a; // 1 传入获取到的临时密钥 (tmpSecret…

一文教你弄懂网络协议栈以及报文格式

文章目录 OSI七层网络协议栈示意图1. 应用层&#xff08;Application Layer&#xff09;2. 表示层&#xff08;Presentation Layer&#xff09;3. 会话层&#xff08;Session Layer&#xff09;4. 传输层&#xff08;Transport Layer&#xff09;5. 网络层&#xff08;Network …

Qt QSerialPort数据发送和接收DataComm

文章目录 Qt QSerialPort数据发送和接收DataComm2.添加 Qt Serial Port 模块3.实例源码 Qt QSerialPort数据发送和接收DataComm Qt 框架的Qt Serial Port 模块提供了访问串口的基本功能&#xff0c;包括串口通信参数配置和数据读写&#xff0c;使用 Qt Serial Port 模块就可以…

【超详细】Plaxis软件简介、 Plaxis Python API环境搭建、自动化建模、Python全自动实现、典型岩土工程案例实践应用

查看原文>>>【案例教程】PLAXIS软件丨自动化建模、典型岩土工程案例解析、模型应用、数据分析、图表制作 目录 第一部分&#xff1a;Plaxis软件简介及 Plaxis Python API环境搭建 第二部分&#xff1a;Plaxis自动化建模-基础案例 第三部分&#xff1a;进阶案例-Pyt…

C# HttpClient 实现HTTP Client 请求

为什么&#xff1f; C# httpclient get 请求和直接浏览器请求结果不一样 为了测试一下HTTP接口的&#xff0c;用C# HttpClient实现了HTTP客户端&#xff0c;用于从服务端获取数据。 但是遇到了问题&#xff1a;C# httpclient get 请求和直接浏览器请求结果不一样 初始代码如…

高德地图绘图,点标记,并计算中心点

效果图 代码如下 / 地图初始化 const map: any ref(null) const marker: any ref(null) const polyEditor: any ref(null) const view: any ref(false) const squareVertices: any ref([]) const init () > {workSpacesCurrent(workspaceId, {}).then((res) > {c…

html+css+js网页设计 旅游 龙门石窟8个页面

htmlcssjs网页设计 旅游 龙门石窟8个页面 网页作品代码简单&#xff0c;可使用任意HTML辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 1&#…

实战案例(5)防火墙通过跨三层MAC识别功能控制三层核心下面的终端

如果网关是在核心设备上面&#xff0c;还能用MAC地址进行控制吗&#xff1f; 办公区域的网段都在三层上面&#xff0c;防火墙还能基于MAC来控制吗&#xff1f; 采用正常配置模式的步骤与思路 &#xff08;1&#xff09;配置思路与上面一样 &#xff08;2&#xff09;与上面区…

分类预测|基于鲸鱼优化-卷积-门控制单元网络-注意力数据分类预测Matlab程序 WOA-CNN-GRU-Attention

分类预测|基于鲸鱼优化-卷积-门控制单元网络-注意力数据分类预测Matlab程序 WOA-CNN-GRU-Attention 文章目录 一、基本原理1. WOA&#xff08;鲸鱼优化算法&#xff09;2. CNN&#xff08;卷积神经网络&#xff09;3. GRU&#xff08;门控循环单元&#xff09;4. Attention&…

计算机毕业设计 基于SpringBoot的课程教学平台的设计与实现 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

IMS中的号码规整 5G注册流程中的语音相关参数

目录 1. IMS中的号码规整 1.1 主要内容 1.2 什么是 IMS 的号码规整及 FAQ 1.3 VoNR(VoLTE) 打 VoNR(VoLTE),被叫号码规整流程 主叫 AS 来做规整 主叫 S-CSCF 来做规整 2. 5G注册流程中的语音相关参数 2.1 主要内容 2.2 使用 VoNR 的第一步:5G注册流程 2.3 5G 注册流…