2024年3月

1 介绍

在互联网场景下,负载均衡(Load Balance)是分布式系统架构设计中必须考虑的一个环节,它通常是指将负载流量(工作任务、访问请求)平衡、分摊到多个操作单元(服务器、组件)上去执行的过程。
目的在于提供负载配比,解决性能、单点故障(高可用)和扩展性(水平伸缩)等问题。
image
以上图为例,随着互联网的兴盛,类似淘宝、京东等网站的访问量逐年提升。原先的单台服务或者单集群模式已经远不能满足需求了,这时候就需要横向扩展多台服务或者多个集群来分摊压力,达到提升系统吞吐的能力,这就是著名的分治理论。
但服务器增加了,他们之间的流量负载也必须有一个组件来管控,这就是负载均衡的作用。负载均衡提供了多种算法策略来满足不同的业务负载需求,下面我们详细来讲解下。

2 几种常见的负载均衡策略

2.1 轮询(Round Robin)

RR轮询,即Round Robin。按照请求的顺序轮流分配到不同的服务器,循环往复。这种策略适用于服务器性能相近的情况,可以平均分配负载。但如果某个服务器性能较差或者偶发故障,会影响整个系统的性能和稳定性。
如下图所示:
image

  • 分别有5条请求过来
  • 按照顺序轮流分配,web-server1 分配到 1、4,web-server2 分配到 2、5,web-server3 分配到 3。

2.2 按照权重轮询(Weighted Round Robin)

即加权轮询,给不同的服务器分配不同的权重,根据权重比例来决定分配请求的数量。这种策略适用于后端服务器性能不均的情况,可以根据实际情况灵活调整。使得性能更好的服务器能够处理更多的请求,从而提高整个系统的处理效率。
如下图所示:
image

  • 分别有5条请求过来
  • web-server1 因为权重为60%,分配到 1、2、3
  • web-server2 权重为20%,分配到 4
  • web-server3 权重为20%,分配到 5

2.3 IP 哈希(IP Hash)

根据客户端的IP地址计算哈希值,将请求分配给特定的服务器,保证相同IP的客户端请求始终发送到同一台服务器。这种策略适用于需要保持客户端会话一致性的场景,例如需要维护用户session的Web应用。
如下图所示:
image

  • IP为192.168.0.99的流量hash计算对应web-service1,所以将1、4流量分配到第1台服务器
  • IP为192.168.0.96、192.168.0.98的流量hash计算对应web-service3,所以将2、3流量分配到第1台服务器

需要注意的是,虽然IP哈希算法可以确保来自同一IP地址的请求被发送到同一台服务器,这在一些需要保持会话一致性的场景中很有用,但它也可能导致负载不均衡。例如,如果某个IP地址发送了大量的请求,那么处理这些请求的服务器可能会过载,而其他服务器可能处于空闲状态。因此,在使用IP哈希算法时,需要仔细考虑其适用性和潜在的风险。需要对极端情况进行评估,笔者就曾经踩过坑。

2.4 最少连接(Least Connections)

将请求分配给当前连接数最少的服务器,以实现负载均衡。这种策略适用于处理长连接请求的场景,如WebSocket、FTP服务。通过记录每台服务器当前正在处理的连接数,将新请求分配给连接数最少的服务器,可以有效避免某些服务器过载导致性能下降的情况。
如下图所示:
image

  • web-service1、web-service2、web-service3的连接数分别是11、15、2,所以 web-service3 相对空闲
  • 1、2、3请求到来的时候,web-service3持续空闲,而web-service1、web-service2持续hold住连接,所以请求分配给web-service3
  • 该算法对服务器性能差异较大的情况有较好的适应性,请求优先发送到连接数较少的服务器,有助于避免某些服务器过载,提升性能。
  • 缺少点就是需要实时监测连接数,并且每个流量来的时候都要判断下再分发,在流量繁忙时增加了服务器开销,影响性能。

2.5 最短响应时间(Least Response Time)

短响应时间(Least Response Time)算法在负载均衡领域中被广泛应用。这种策略适用于对响应时间有严格要求的应用场景。通过实时监测每台服务器的响应时间,将请求分配给响应时间最短的服务器,可以确保用户获得最快的响应,提升用户体验。
如下图所示:
image
同样,这种算法也有自己的优缺点:

优点

1、
提高用户体验
:通过选择响应时间最短的服务器来处理请求,可以显著减少用户的等待时间,提高整体的用户体验。
2、
动态负载均衡
:该算法能够实时地根据服务器的响应时间来调整负载分配,确保每台服务器都能根据其实际性能来处理相应数量的请求。
3、
处理高峰期流量
:在流量高峰期,最短响应时间算法可以确保请求被迅速处理,避免系统拥堵和延迟。

缺点

1、
计算开销
:为了确定每台服务器的响应时间,系统需要不断地进行监测和计算,这可能会增加额外的系统开销。
2、
瞬时波动
:由于该算法主要依赖于实时的响应时间,因此可能会受到瞬时波动的影响。例如,如果某台服务器在某一时刻由于某种原因(如临时的高负载)响应时间变长,它可能会被暂时排除在负载均衡之外,即使其实际性能可能仍然优于其他服务器。
3、
可能忽略其他性能指标
:最短响应时间算法主要关注响应时间,可能忽略了其他重要的性能指标,如服务器的处理能力、内存占用等。在某些情况下,这可能导致负载分配不够均衡。

3 总结

本文介绍了常见的几种负载均衡策略,此外,还有一些其他策略,如:

  • DNS负载均衡,适用于全球范围内的负载均衡,可以根据用户的地理位置将请求分发到最近的服务器,提高访问速度。
  • 数据层负载均衡,需要考虑“数据与请求均衡的平衡”,最常见的方式就是按照分库分表进行分片hash负载

在选择负载均衡策略时,需要根据实际应用场景、服务器性能、网络状况等因素进行综合考虑,以达到最佳的负载均衡效果。

FastWiki 发布 v0.1.0

https://github.com/239573049/fast-wiki/releases/tag/v0.1.0

更新日志

  • 兼容
    OpenAI
    接口格式
  • 删除Blazor版本UI
  • 删除
    useEffect,
  • 解决可能存在问题的bug
  • 修复对话可以看到所有对话
  • Merge branch 'master' of
    https://gitee.com/hejiale010426/fast-wiki
  • 更新文档
  • 更新加入群聊地址
  • 修改删除
  • Merge pull request #12 from 239573049/feature/ui-bug
  • 适配手机端分享界面
  • 优化前端代码逻辑
  • 修复staticcode错误
  • Merge branch 'master' of
    https://gitee.com/hejiale010426/fast-wiki
  • 优化打包体积
  • 修改文档
  • 增加静态文件压缩中间件
  • 修复对话框bug

快速入门

下载
docker-compose.yml
脚本

curl https://gitee.com/hejiale010426/fast-wiki/raw/master/docker-compose.yml -O docker-compose.yml

下载完成以后会在当前目录下生成一个
docker-compose.yml
文件,当然默认下载的并不能直接使用,我们使用nano编辑文件的内容:

nano docker-compose.yml

文件大概内容如下 ,如果你有代理的话则修改
OPENAI_CHAT_ENDPOINT

OPENAI_CHAT_EMBEDDING_ENDPOINT
的地址为你的代理地址,格式是
http://ip:端口
即可,然后
OPENAI_CHAT_TOKEN
需要提供您的OpenAI的Key或您的代理的Token,其他的都默认即可,
ASPNETCORE_ENVIRONMENT=Development
则会自动迁移数据库,并且下一个版本的更新也会自动更新,第一次执行务必使用。

version: '3.8'  # 可以根据需要使用不同的版本
services:
  fast-wiki-service:
    image: registry.cn-shenzhen.aliyuncs.com/fast-wiki/fast-wiki-service
    container_name: fast-wiki-service
    user: root
    restart: always
    ports:
      - "8080:8080"
    build: 
      context: .
      dockerfile: ./src/Service/FastWiki.Service/Dockerfile
    depends_on:
      - postgres
    volumes:
      - ./wwwroot/uploads:/app/wwwroot/uploads
    environment:
      - OPENAI_CHAT_ENDPOINT=https://api.openai.com
      - OPENAI_CHAT_EMBEDDING_ENDPOINT=https://api.openai.com
      - OPENAI_CHAT_TOKEN={您的TokenKey}
      - OPENAI_CHAT_MODEL=gpt-3.5-turbo
      - OPENAI_EMBEDDING_MODEL=text-embedding-3-small
      - ASPNETCORE_ENVIRONMENT=Development

  postgres: # 当前compose服务名
    image: registry.cn-shenzhen.aliyuncs.com/fast-wiki/pgvector:v0.5.0 # 拉取的数据库镜像
    container_name: postgres  # 容器运行的容器名称
    restart: always  # 开机自启动
    environment:  # 环境变量
      POSTGRES_USER: token  # 默认账号
      POSTGRES_PASSWORD: dd666666 # 默认密码
      POSTGRES_DB: wiki # 默认数据库
      TZ: Asia/Shanghai  # 数据库时区
    volumes:
      - ./postgresql:/var/lib/postgresql/data # 将PostgreSql数据持久化

修改完上面的配置然后将容器执行启来:

docker-compose up -d

然后我们访问启用的访问的端口
http://ip:8080
, 进入首页以后点击立即开始。

然后登录系统,默认的账号
admin
密码
Aa123456
,登录成功以后再点击立即开始,则进入首页。

然后点击右上角的新增,输入测试应用名称,添加完成,点击左边菜单的第一个对话。

进入对话就可以于AI进行对话了:

上面只是简单的入门了FastWiki的对话功能,更多功能可以自行搭建测试。

结尾

我们期待您的反馈和贡献!

前言

作为开发人员,我们经常向应用程序添加新功能并修改当前的
Api
。版本控制使我们能够安全地添加新功能而不会造成中断性变更。一个良好的
Api
版本控制策略可以清晰地传达所做的更改,并允许使用现有
REST Api
的客户端在准备好时才迁移或更新他们的应用程序到最新版本。

哪些行为可能会造成 Api 的中断性变更呢?

  • 删除或重命名 Api
  • 修改 Api 参数(类型,名称,可选参数变成非可选参数,删除必需参数等)
  • 更改现有 Api 的行为
  • 更改 Api 响应
  • 更改 Api 错误代码
  • More

我们在做开发的过程中迟早会面对 Api 版本控制需求,在 Api 开发的过程中学习如何进行版本控制是至关重要的。

本文主要介绍在 MinimalApis 进行版本控制,官网文档在文末

借助
aspnet-api-versioning
帮助
Minimalapis
实现版本控制

开始之前在项目中安装两个 nuget 包:

Install-Package Asp.Versioning.Http
Install-Package Asp.Versioning.Mvc.ApiExplorer
  • Asp.Versioning.Http
    用于在
    MinimalApis
    中提供版本控制支持
  • Asp.Versioning.Mvc.ApiExplorer
    用于
    OpenApi
    ,格式化路由版本参数等

配置详情

builder.Services.AddApiVersioning(options =>
{
    options.DefaultApiVersion = new ApiVersion(2, 0);//默认版本
    options.ReportApiVersions = true;//Response Header 指定可用版本
    options.AssumeDefaultVersionWhenUnspecified = true;//如果没有指定版本用默认配置
    options.ApiVersionReader = ApiVersionReader.Combine(
      new QueryStringApiVersionReader("api-version"),//QueryString
      new HeaderApiVersionReader("X-Version"),//Header
      new MediaTypeApiVersionReader("ver"),//Accept MediaType
      new UrlSegmentApiVersionReader());//Route Path
}).AddApiExplorer(options =>
{
    options.GroupNameFormat = "'v'VVV";
    options.SubstituteApiVersionInUrl = true;
});

AddApiVersioning
提供了一个委托参数
Action<ApiVersioningOptions>
来对
Api 版本控制配置
,下面看主要参数的配置解释

  • DefaultApiVersion

    options.DefaultApiVersion = new ApiVersion(2,0);
    

    指定 Api 的默认版本以上设置为 2.0 版本,默认是 1.0

  • ReportApiVersions

     options.ReportApiVersions = true;//Response Header 指定可用版本
    

    在 ResponseHeader 中指定当前 Api 可用的版本 默认不开启

  • AssumeDefaultVersionWhenUnspecified

    options.AssumeDefaultVersionWhenUnspecified = true;
    

    开启后 如果
    Api
    不指定版本默认
    DefaultApiVersion
    设置版本,适合已经存在的服务开启版本控制,帮助在不破坏现有客户端的情况下改进现有服务并集成正式的
    Api

  • ApiVersionReader

    
        options.ApiVersionReader = ApiVersionReader.Combine(
        new QueryStringApiVersionReader("api-version"),//QueryString 默认参数 api-vesion
        new HeaderApiVersionReader("X-Version"),//Header 默认v
        new MediaTypeApiVersionReader("ver"),//Accept MediaType 默认参数v
        new UrlSegmentApiVersionReader());//Route Path 参数v
    

    配置如何读取客户端指定的 Api 版本,默认为
    QueryStringApiVersionReader
    即使用名为
    api-version
    的查询字符串参数。
    从上面可以看出有四种开箱即用的 Api 服务版本定义方式


    • QueryStringApiVersionReader:
      https://localhost:7196/api/Todo?api-version=1
    • HeaderApiVersionReader:
      https://localhost:7196/api/Todo -H 'X-Version: 1'
    • MediaTypeApiVersionReader:
      GET api/helloworld HTTP/2
      Host: localhost
      Accept: application/json;ver=1.0
      
    • UrlSegmentApiVersionReader:
      https://localhost:7196/api/workouts?api-version=1

    可以通过
    ApiVersionReader.Combine
    联合使用。

虽然
aspnet-api-versioning
提供了多种版本控制的方式,但是在我们实际项目开发的过程中,我们尽可能只采用一种方案,只用一种标准可以让我们版本开发更加的容易维护,而且多种方案配置默认策略 对
OpenApi
的集成和版本控制的默认行为都互有影响。

以上四种方案只有
QueryStringApiVersionReader

UrlSegmentApiVersionReader
符合
Microsoft REST Guidelines
的规范,所以我们只需要上面选一个即可.

MinimalApis 版本控制

我们采用其中的一种 来做演示看看
ApiVesioning
是如何实现的,就按默认行为 QueryStringApiVersionReader 来做一个简单的 Demo。

创建一个 MinimalApi 的项目

VS 创建新项目->输入项目名字然后点击下一步->
使用控制器的 CheckBox 确定取消勾选
.Net Cli 安装 nuget 或者 VS 包管理器

dotnet add package Asp.Versioning.Http
dotnet add package Asp.Versioning.Mvc.ApiExplorer

Program.cs
添加默认配置

builder.Services.AddProblemDetails();
builder.Services.AddApiVersioning(options =>
{
    options.DefaultApiVersion = new ApiVersion(2, 0);//默认版本
    options.ReportApiVersions = true;//Response Header 指定可用版本
    options.AssumeDefaultVersionWhenUnspecified = true;//如果没有指定版本用默认配置
}).AddApiExplorer(options =>
{
    options.GroupNameFormat = "'v'VVV";
    options.SubstituteApiVersionInUrl = true;
});

aspnet-api-versioning
的异常处理机制依赖
ProblemDetails
,
所以
builder.Services.AddProblemDetails();
必须要注册到 IOC 容器。
AddApiVersioning
没有注册任何的
ApiVersionReader
,所以会用默认的
QueryStringApiVersionReader
的模式。

AddApiExplorer

OpenApi
对接口格式化的策略配置

认识 几个核心方法

  • NewVersionedApi :创建一个路由组建造者,用于定义 Api 中所有版本化端点。
  • HasApiVersion :表示 ApiVersionSet 支持指定的 ApiVersion。

  • HasDeprecatedApiVersion :配置废弃指定的 Api 版本。

  • MapToApiVersion : 将指定的 Api 版本映射到配置的端点。

  • IsApiVersionNeutral : 版本无关 也可以说任何的版本都可以访问到这个终结点

添加 Api EndPoint


{
    var todoV1 = app.NewVersionedApi("Todo")
         .HasDeprecatedApiVersion(new ApiVersion(1, 0));//过期版本
    var todoGroup = todoV1.MapGroup("/api/Todo");
    todoGroup.MapGet("/", () => "Version 1.0").WithSummary("请用V2版本代替");
    todoGroup.MapGet("sayhello", (string name) => $"hello {name}").
}
{
    var todoV2 = app.NewVersionedApi("Todo")
                         .HasApiVersion(new ApiVersion(2, 0));
    var todoGroup = todoV2.MapGroup("/api/Todo");
    todoGroup.MapGet("/", () => "Version 2.0").MapToApiVersion(new ApiVersion(2, 0)).WithSummary("Version2");
}
{
    var todoV3 = app.NewVersionedApi("Todo")
            .HasApiVersion(new ApiVersion(3, 0));
    var todoGroup = todoV3.MapGroup("/api/Todo");
    todoGroup.MapGet("/", () => "Version 3.0").WithSummary("Version3");
    IsApiVersionNeutral();
}

上面定义 Todo 的相关业务,当前有三个版本,V1 已经过期不推荐使用,V2 是主要版本,V3 是预览开发版本,
IsApiVersionNeutral
标注了一个
sayHello
接口是跟版本无关的

Run
项目 测试一下


访问
api/Todo
,Options 配置了默认版本为 2.0
https://localhost:7141/api/todo
返回 Version 1.0 符合预期
image


测试 V1 版本
https://localhost:7141/api/todo?api-version=1.0
返回 Version 1.0 符合预期且 ResponseHeader 标记了过期版本和受支持的版本
image

image


测试 V2 版本
https://localhost:7141/api/todo?api-version=2.0
可以看到 返回 Version 2.0 符合预期
image


测试 V3 版本
https://localhost:7141/api/todo?api-version=3.0
可以看到 返回 Version 3.0 符合预期
image


测试 sayHello (版本无关)

  • https://localhost:7141/api/Todo/sayhello
  • https://localhost:7141/api/Todo/sayhello?name=ruipeng& api-vesion=1.0
  • https://localhost:7141/api/Todo/sayhello?name=ruipeng&api-vesion=2.0
  • https://localhost:7141/api/Todo/sayhello?name=ruipeng&api-vesion=3.0
    image


到这儿基本可以实现我们的需求了,在
aspnet-api-versioning
中还提供了
NewApiVersionSet
的方法配置添加实现
Api
的管理,大家也可以尝试下。

版本管理对接 OpenApi

刚才我们的项目
Run
起来之后
Swagger
首页看到只有
V1
版本的界面,我们来设置一下让他支持
Swagger
界面版本切换

创建 ConfigureSwaggerOptions 添加多个 SwaggerDoc

public class ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) : IConfigureOptions<SwaggerGenOptions>
{
    public void Configure(SwaggerGenOptions options)
    {
        foreach (var description in provider.ApiVersionDescriptions)
        {
            options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
        }
    }

    private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
    {
        var text = new StringBuilder("An example application with OpenAPI, Swashbuckle, and API versioning.");
        var info = new OpenApiInfo()
        {
            Title = "MinimalApis With OpenApi ",
            Version = description.ApiVersion.ToString(),
            Contact = new OpenApiContact() { Name = "Ruipeng", Email = "478083649@qq.com" },
            License = new OpenApiLicense() { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") }
        };

        if (description.IsDeprecated)
        {
            text.Append(" This API version has been deprecated.");
        }

        if (description.SunsetPolicy is SunsetPolicy policy)
        {
            if (policy.Date is DateTimeOffset when)
            {
                text.Append(" The API will be sunset on ")
                    .Append(when.Date.ToShortDateString())
                    .Append('.');
            }

            if (policy.HasLinks)
            {
                text.AppendLine();

                for (var i = 0; i < policy.Links.Count; i++)
                {
                    var link = policy.Links[i];

                    if (link.Type == "text/html")
                    {
                        text.AppendLine();

                        if (link.Title.HasValue)
                        {
                            text.Append(link.Title.Value).Append(": ");
                        }

                        text.Append(link.LinkTarget.OriginalString);
                    }
                }
            }
        }

        info.Description = text.ToString();

        return info;
    }
}

依赖注入

builder.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();

创建拦截器

public class SwaggerDefaultValues : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        var apiDescription = context.ApiDescription;

        operation.Deprecated |= apiDescription.IsDeprecated();

        foreach (var responseType in context.ApiDescription.SupportedResponseTypes)
        {
            var responseKey = responseType.IsDefaultResponse ? "default" : responseType.StatusCode.ToString();
            var response = operation.Responses[responseKey];

            foreach (var contentType in response.Content.Keys)
            {
                if (!responseType.ApiResponseFormats.Any(x => x.MediaType == contentType))
                {
                    response.Content.Remove(contentType);
                }
            }
        }

        if (operation.Parameters is null)
        {
            return;
        }


        foreach (var parameter in operation.Parameters)
        {
            var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name);

            parameter.Description ??= description.ModelMetadata?.Description;

            if (parameter.Schema.Default is null &&
                 description.DefaultValue is not null &&
                 description.DefaultValue is not DBNull &&
                 description.ModelMetadata is ModelMetadata modelMetadata)
            {

                var json = JsonSerializer.Serialize(description.DefaultValue, modelMetadata.ModelType);
                parameter.Schema.Default = OpenApiAnyFactory.CreateFromJson(json);
            }

            parameter.Required |= description.IsRequired;
        }
    }
}

Swagger 依赖注入

builder.Services.AddSwaggerGen(options => options.OperationFilter<SwaggerDefaultValues>());

UseSwaggerUI 添加 Swagger 终结点

    app.UseSwaggerUI(options =>
    {
        var descriptions = app.DescribeApiVersions();
        // build a swagger endpoint for each discovered API version
        foreach (var description in descriptions)
        {
            var url = $"/swagger/{description.GroupName}/swagger.json";
            var name = description.GroupName.ToUpperInvariant();
            options.SwaggerEndpoint(url, name);
        }
    });

Run
Swagger 查看项目

image

左上角可以成功切换版本,OpenApi 版本管理成功

最后

本文的
demo
用了
aspnet-api-versioning
版本控制的一种方式来做的演示,
WebApi Controller
配置好
Options
之后只需要用
aspnet-api-versioning
提供的
Attribute
就可以实现版本管理,
Route Path

httpHeader
等传参数的方式只需要微调就可以实现,更多高级功能请浏览
aspnet-api-versioning
官网(文末有官网地址)。

Api
版本控制是设计现代
Api
的最佳实践之一。从第一个版本开始实现
Api
版本控制,这样可以更容易地让客户端支持未来的
Api
版本,同时也让您的团队习惯于管理破坏性变化和对
Api
进行版本控制。

以下是本文的完整
源代码

aspnet-api-versioning
官网学习文档

本文分享自华为云社区《
【云驻共创】手把手的使用Toolkit插件在诗情画意中完成AI诗朗诵
》,作者: 红目香薰。

云原生时代,开发者们的编程方式、编程习惯都发生了天翻地覆的变化,大家逐渐地习惯在云端构建自己的应用。作为新一代的开发者们,如何更快速了解云,学习云,使用云,更便捷、更智能的开发代码,从而提升我们的开发效率,是当前最热门的话题之一,而Huawei Cloud Toolkit,作为连接华为云的百宝箱,是集成在各大IDE平台上的插件集合,会在方方面面提升着开发者的效率。

插件介绍

本段落主要介绍华为云的以下三块内容:
1、和云端建立链接——华为云API开发套件
2、开发更"智能"——华为云CodeArts Snap
3、守护代码质量与安全——华为云CodeArts Check

华为云API开发套件助力开发者快速集成华为云,可做到便捷连接200+的华为云服务,引用7000+的华为云API服务,在IDE中集成华为云的功能,让开发者与云端华为云建立连接。

智能编码方面集成了华为云自主研发的代码大模型和软件分析技术,全场景函数级、行级代码生成,同规模算力一次通过业界第一,这会帮助开发者更为高效的使用自动语义生成,达到快速开发的目的,使整个过程更智能。

代码质量与安全上华为云CodeArts Check插件可以做到守护代码的质量与安全,帮助开发者精准的实施代码检查,以及提供了代码的一键格式化、一键自动修复的功能,可以确保编程规范的落地。IDE插件与云端服务构筑了三层代码防护体系。

1、华为云API:云原生开发者必备的coding利器

华为云通过API提供服务全面开放能力,为开发者构建华为云应用和变现基础,是开发者核心关注的"生产要素"之一,各云服务提供SDK,帮助开发者快速集成API,面对海量的各种各样的开发者,华为云提供多种语言SDK应用到各行各业,面对各行各业海量诉求,如何更快速、更便捷地找到契合的API,并且快速集成到应用中,成为开发者提升开发效率的当务之急。

华为云的OpenAPI提供了各类的产品服务,如计算类、存储类、人工智能类、运营类、应用中间件、管理与监管等等诸多的功能品类的API服务可以让我们快速的找到我们需要的具体需求来解决我们的各类问题。并且有着大量的错误码提示,加快异常问题的解决高速、高效的完成我们的开发需求。

可以在人工智能的开放接口中看到,自然语言处理、人机对话、人脸识别、图像识别、推荐系统等等的功能可以更好的通过AI的形式帮助我们提高工作的效率。为开发者打造更好的应用云体验。

在API插件中,华为云提供了如下功能:

  • 对接华为云API开放平台,全面覆盖华为云200+云服务、7000+API, 支持用户检索API、查看API文档、调试API、 以及提供SDK示例代码供用户学习如何使用API。
  • 提供华为云SDK代码片段补全功能,SDK依赖包自动引入,加速用户集成华为云API。
  • 对接华为云开发体验馆Codelabs, 提供500+云服务代码示例,向导式教程帮助用户快速学习。

华为云提供的API插件支持多IDE的平台,可以让我们开发更加得心应手,以及对应的热门开发语言代码的补全支持,下图可以看到常见的语言Java、Python、Go、JavaScript等语言都是支持的。

例如我们拿PyCharm Community Edition 举例,可以看到我们点开插件安装的时候搜索Huawei就能看到华为的猪都插件都可以下载使用。

这里我们看到第一个就是CodeArts Snap智能编程助手,接下来我们就来介绍一下它吧。

2、华为代码大模型的应用:华为云CodeArts Snap智能编程助手

沉淀华为自主研发代码大模型和软件分析技术,以IDE插件形式赋能开发者高效、可信开发,支持主流编程语言和IDE生态,大体分为JetBrains的IDE生态与Visual Studio的IDE生态,下图为两种生态工具的截图:

华为云CodeArts Snap智能编程助手插件的核心价值特性有:

1、全场景函数级、行级代码生成。

2、支持中英文描述,通过自然语言一键生成完整函数级代码

3、同规模同算力一次通过率业界第一

4、专业开发者编程量减少20%

协同华为云CodeArts软件开发平台服务政府、国央企、传统大企和互联网企业。

Snap在软件研发生产线起到非常重要的作用。

3、华为云CodeArts Check IDE插件:实时精准守护软件质量与安全

华为云Check插件,助力开发者实时精准检查,在Check插件中,华为云提供了如下功能:
  • 集成华为云Check云服务轻量级检查规则,精准覆盖质量类、风格类与安全类关键检查能力,与云服务共筑三层代码防护体系,兼顾效率与质量。
  • 提供丰富的过滤选项,支持用户分级分类查看规则,加速用户学习了解规则的使用。
  • 支持文件/文件夹/项目级扫描,同时提供一键格式化能力及自动修复功能。
  • 提供详细的缺陷修复指导,包括缺陷描述信息,遵从规范,正确示例,错误示例,修改建议。
  • 兼容多平台: VSCodelDE、 Intellij IDEA、 CodeArts IDE; 并提供Java, C, C++ 3种语言的检测能力。
支持华为云CodeArts Check云服务构筑三级防护体系,高效开展代码检查活动。

Huawei Cloud Toolkit初体验

体验地址:https://developer.huaweicloud.com/develop/toolkit.html?utm_source=hw_zz&ticket=ST-8956666661-XM1Xi9kdd7fzZxXe4NwquHSE-sso&locale=zh-cn

迈向云原生之路:华为云Toolkit实战

实战一Huawei Cloud API:

开发者在IntelliJ IDEA上,基于华为云NLP自然语言处理服务API实现一个AI作诗的应用。

一、准备工作

环境搭建:下载安装IntelliJ IDEA,配置好maven、JDK等环境

插件安装:文件-> 设置->插件,下载安装Huawei Cloud API插件

二、主要流程:

1.选:打开Huawei Cloud API插件,选择"人工智能"->"自然语言处理" ->"语言生成服务"-> "RunPoem (诗歌生成)"

2.学:仔细查看接口详情,包括输入参数和输出模型定义,以及SDK示例代码

3.写:在业务代码需要调用AP|的地方,直接输入RunPoem, IDE会自动补全调用代码,也将依赖SDK包导入至工程

4.Debug:调试写好的代码,查看API返回结果是否符合预期

扩展内容:大家可以选择插件中展示的其他有意思的API进行调试开发,比如人脸识别、文字识别、图像识别服务的API等等, 包括且不限于:可以使用自然语言处理服务的AP|来判断一段文本的情感表达; 使用人脸识别服务的API进行人脸比对,人脸检测;使用文字识别,服务的API进行证件图片文字扫描提取;使用图像识别服务的API分析并识别图片中包含的政治人物、明星及网红人物。

实战一操作过程

首先需要先在插件商店安装华为云的Cloud API插件,点击【Install】安装即可,安装完毕需要重新启动计算机。

安装完毕后可以看到华为的标识:

需要登录账号:

登录成果效果:

为了演示方便,我们下载一个中文插件:

这回看着中文演示方便多了。

我们使用诗歌生成的API:

为了操作方便,我们选择Python语言来测试。

复制代码后直接引入到编辑文件中,这里能看到有没有的库需要我们单独引入。

调用前需要开通服务:https://console.huaweicloud.com/nlp/#/nlp/overview

开通成功提示:

调试效果:

再来一次:

"红目香薰",
"红心不作小桃粧,",
"笑倚东风百媚香。",
"若使落花深见妒,",
"一朝应怨夕阳长。"

代码测试,这里少什么包直接pip安装什么包就OK了,国内的华为镜像,超级块。

不报错后需要单独的签名一下:

签名地址:https://support.huaweicloud.com/devg-apisign/api-sign-provide.html

我的凭证

添加秘钥

可以看到已开通

有了对应的AK/SK就能访问了,运行成功效果:

到这里呢,第一个实战就结束了,后面的实战对应的开通以及AK/SK获取与这个操作步骤相同。

实战二CodeArts Snap:

主题说明:开发者在IntellijJ IDEA上,基于华为云CodeArts Snap智能编程助手实现一个语音合成的应用

一、准备工作

环境搭建:下载安装IntelliJ IDEA,配置好maven、JDK等环境

插件安装:文件-> 设置->插件,下载安装Huawei Cloud CodeArts Snap插件

二、主要流程:

1.搜:打开Huawei Cloud API插件,搜素关键字"语音合成"

2.选:找到由SIS云服务提供一个语音合成接口RunTts, 查看接口功能描述,确认是想要的

3.学:仔细查看接口详情,包括输入参数和输出模型定义

4.用:输入参数,使用API插件的在线调试能力,查看接口返回值

5.写:使用Huawei Cloud CodeArts Snap插件,输入想要实现的逻辑作为注释,快捷键Alt +C,选择想要生成的代码

6.Debug:调试写好的代码,完成语音合成应用

扩展内容:大家可以选择插件中展示的其他有意思的API进行调试开发,同时也可以在自己的代码工程中使用CodeArts Snap插件,包括且不限于直接ALT+C/Enter根据上下文智能生成代码;输入注释,按ALT+C/Enter生成根据注释内容和上 下文智能生成代码。

实战二操作过程

先安装插件啊,安装完成可以看到右侧菜单栏中就有了CodeArts Snap插件了。

找到对应的语音合成接口。

API操作:

这里要写上text,也就是需要合成的文本内容,下面的选项是生成文件的格式,采样率以及使用的语音音色。

调试测试成功。

运行后可以正常播放:

源码留在这里,我写了将base64字符串解码为字节流的函数。

# coding: utf-8 import base64 from huaweicloudsdkcore.auth.credentials import BasicCredentials from huaweicloudsdksis.v1.region.sis_region import SisRegion from huaweicloudsdkcore.exceptions import exceptions from huaweicloudsdksis.v1 import * def base64_to_file(base64_string, file_path): # 将base64字符串解码为字节流 decoded_bytes = base64.b64decode(base64_string) # 将字节流写入文件 with open(file_path, 'wb') as file: file.write(decoded_bytes) if __name__ == "__main__": # The AK and SK used for authentication are hard-coded or stored in plaintext, which has great security risks. It is recommended that the AK and SK be stored in ciphertext in configuration files or environment variables and decrypted during use to ensure security. # In this example, AK and SK are stored in environment variables for authentication. Before running this example, set environment variables CLOUD_SDK_AK and CLOUD_SDK_SK in the local environment ak = "OPZYI*******4" sk = "t7XDJTV8MeOf***************34yh4OVdI" projectId = "e10b8f0d1e59477cb65ab1c6ad1d6eac" credentials = BasicCredentials(ak, sk, projectId) client = SisClient.new_builder() \ .with_credentials(credentials) \ .with_region(SisRegion.value_of("cn-east-3")) \ .build() try: request = RunTtsRequest() configbody = TtsConfig( audio_format="mp3", sample_rate="16000", _property="chinese_xiaoyan_common" ) request.body = PostCustomTTSReq( config=configbody, text="期待新的一年您更继续创造更多的价值——红目香薰·留" ) response = client.run_tts(request) # 将base64转换成文件 print(response.result.data) base64_to_file(response.result.data, "a.mp3") except exceptions.ClientRequestException ase: print(e.status_code) print(e.request_id) print(e.error_code) print(e.error_msg)
字打错了,但是直接生成了。能证明一点,肯定是刚刚生成的。我们最后来根据实战一生成的诗句来朗读一下,到这里实验二又结束了。

实战三CodeArts Check:

主题说明:开发者在IntelliJ IDEA上,基于华为云CodeArts Check进行代码检查

一、准备工作

环境搭建:下载安装IntelliJ IDEA,配置好maven、JDK等环境

插件安装:文件-> 设置->插件,下载安装Huawei Cloud CodeArts Check插件

二、主要流程:

1.用:打开目标检查代码文件,鼠标右键呼出菜单,点击"CodeArts Check文件检查"。

2.学:侧边栏点击"CodeArts Check"打开窗口,查看文件代码问题检查结果。

3.写:点击问题查看规则详情,按照说明修改代码,写出安全、简洁、规范的代码。

扩展内容:大家可以在设置窗口自定义代码检查规范,目前支持C++&Java;插件支持文件检查、项目检查、格式化,也可以指定目录进行检查

实战三操作过程

先安装CodeArts Check插件

安装完毕还是需要重启的。

使用检查实战一二的代码。

点击就可以操作,或者鼠标右键选择文件检查,我们更换一个代码测试一下,发现已经找到其中的优化方案了。

这里报的是代码规范:

至此,我们第三个实战又结束了。

总结

我们在整篇文章中了解了华为云的诸多插件以及API接口的使用,并且结合实战一与实战二将AI生成的诗词进行了朗读的操作,在文章中也穿插了音频,可以试听一下。

我们通过华为云插件的使用可以大大的提高工作以及开发的效率,并且我们在华为云所提供的接口中可以看到诸多的分类与功能,很多功能都能拿过来直接使用,方便我们的工作,当我们把华为云所提供的API使用熟练后相信一定会为您带来很多意想不到的价值。

点击关注,第一时间了解华为云新鲜技术~

(适用于.NET/.NET Core/.NET Framework)
【目录】
0.前言
1.第一个AOP程序
2.Aspect横切面编程
3.一个横切面程序拦截多个主程序
4.多个横切面程序拦截一个主程序
5.优势总结
6.展望


0.前言
AOP(Aspect Oriented Programming)是“面向横切面编程”,主要是用来对程序/模块进行解耦。怎么理解??

我们可以把一般的编程理解为“纵向编程”(主程序),比如如下的一个示例代码:

        public string GetInfo(inti)
{
string s = "";if (i == 1)
s
= "A";else if (i == 2)
s
= "B";else if (i == 3)
s
= "C";elses= "Z";returns;
}

试想一下,上述软件实际使用后,

  • 如果条件变量i有更多的判断值,我们是不是要在GetInfo()方法内部修改代码+重新编译?
  • 如果后续需要加个日志记录功能,我们是不是也要在GetInfo()方法内部加上日志函数+重新编译?
  • 如果...
  • 更多如果...

为了避免上述的这些麻烦并增加软件的灵活性,“横向编程”,也就是AOP被创造了出来,它就像是“横切一刀”,把相关功能塞进了主程序。

现行AOP的实现,主要是通过拦截方法(即拦截主程序),并修改其参数+返回值来完成。

网上有很多相关方案,比如:特性注释拦截、动态代码生成、派遣代理模式、等。
但这些方案要么实现的很复杂、要么耦合度没完全切断、逻辑有变化时还是需要修改少量代码重新编译。均不够理想

而今天要隆重登场的主角-DeveloperSharp平台中的AOP技术,则提供了一种简便、快捷、彻底解耦的AOP实现。使用它,在程序逻辑有变化时,你只需要修改配置文件就行,而不再需要对主程序进行一丁丁点的代码修改!!


1.第一个AOP程序

制作一个AOP程序需要四个步骤:

(1)制作主程序

(2)制作横切面程序

(3)制作配置文件,让横切面程序拦截主程序

(4)调用主程序

下面,我们一步一步来实现上述四个步骤。

【第一步】:制作主程序

我们在Visual Studio中新建一个名为“School.Logic”的类库工程,并在该工程中新建一个名为PersonManage的类,该类中有一个名为GetInfo1的方法,代码如下:

//从NuGet引用DeveloperSharp包
usingDeveloperSharp.Structure.Model;namespaceSchool.Logic
{
//主程序必须继承自LogicLayer类 public classPersonManage : LogicLayer
{
public string GetInfo1(string Name, intNum)
{
return $"共有{Name}{Num}人";
}
}
}

以上,编写了一个非常简单的主程序。

【第二步】:制作横切面程序

我们再在Visual Studio中新建一个名为“School.Aspect”的类库工程,并在该工程中新建一个名为Interceptor1的类,代码如下:

//从NuGet引用DeveloperSharp包
usingDeveloperSharp.Structure.Model.Aspect;namespaceSchool.Aspect
{
//横切面程序必须继承自AspectModel类 public classInterceptor1 : AspectModel
{
//PreProcess方法先于主程序执行 public override void PreProcess(objectsender, AspectEventArgs e)
{
//把主程序的两个参数值改掉 e.MethodInfo.ParameterValues[0] = "老师";
e.MethodInfo.ParameterValues[
1] = 20;
}
//PostProcess方法后于主程序执行 public override void PostProcess(objectsender, AspectEventArgs e)
{

}
}
}

以上,编写了一个横切面程序。它的主要功能是把主程序方法的两个参数值给改掉。

【第三步】:制作配置文件,让横切面程序拦截主程序

若是在.Net Core环境下,我们创建一个名为DeveloperSharp.json的配置文件,设置让Interceptor1拦截PersonManage中的GetInfo1方法。文件内容如下:

{"DeveloperSharp":
{
"AspectObject":
[
{
"name":"School.Aspect.Interceptor1", //横切面拦截器类 "scope":"School.Logic.PersonManage", //被拦截的主程序类 "method":"GetInfo1" //被拦截的方法 }
]
}
}

若是在.Net Framework环境下,我们创建一个名为DeveloperSharp.xml的配置文件,设置让Interceptor1拦截PersonManage中的GetInfo1方法。文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
<DeveloperSharp>
  <AspectObject>
    <Aoname="School.Aspect.Interceptor1"scope="School.Logic.PersonManage"method="GetInfo1"/>
  </AspectObject>
</DeveloperSharp>

注意:以上配置中所有的类名,都要用完全限定名。

【第四步】:调用主程序

最后,我们再在Visual Studio中创建一个控制台工程,让它来调用主程序中的GetInfo1方法,代码如下:

        //需要引用School.Aspect、School.Logic、DeveloperSharp三项
        static void Main(string[] args)
{
var pm = newSchool.Logic.PersonManage();//要用这种形式调用主程序中的方法,AOP功能才会生效 var str = pm.InvokeMethod("GetInfo1", "学生", 200);
Console.WriteLine(str);

Console.ReadLine();
}

为了让前面第三步创建的配置文件生效,我们此时还需要在此主调项目中对它进行链接:

若是在.Net Core环境下,我们只需要把DeveloperSharp.json文件放到程序执行目录中(即bin目录下与dll、exe等文件的同一目录中,放错了位置会报错)(注意:有些.Net Core版本在Visual Studio“调试”时,不会在bin目录下生成全部的dll、exe,此时需要把此配置文件放在应用程序的“根目录”下)。
若是在.Net Framework环境下,我们需要在工程配置文件App.config/Web.config中添加appSettings节点,节点内容如下:

  <appSettings>
    <addkey="ConfigFile"value="D:\Test\Assist\DeveloperSharp.xml" />
  </appSettings>

此处需要设置为配置文件的“绝对路径”(使用“绝对路径”而不是“相对路径”,一是有利于安全性,二是有利于分布式部署)

一切准备完毕,运行,结果如下:

【控制台显示出】:
共有老师20人

可见AOP已经拦截成功。

若此时,我们在配置文件DeveloperSharp.json/DeveloperSharp.xml中稍做修改,比如:把“GetInfo1”这个方法名改为“ABC”这样一个不存在的方法名,再运行,结果如下:

【控制台显示出】:
共有学生200人

注意:使用DeveloperSharp平台的AOP技术,主程序类库名必须以Logic结尾(本篇为:School.Logic),横切面程序类库名必须以Aspect结尾(本篇为:School.Aspect),且这两个名字的其它部分要完全一样。这是一个特别限定条件!

2.Aspect横切面编程

上面,第二步,制作的横切面程序,是通过修改主程序方法的参数值,而最终改变了主程序的返回值。

其实,我们也有办法直接修改主程序方法的返回值,比如把上面Interceptor1类的代码修改为如下:

//从NuGet引用DeveloperSharp包
usingDeveloperSharp.Structure.Model.Aspect;namespaceSchool.Aspect
{
//横切面程序必须继承自AspectModel类 public classInterceptor1 : AspectModel
{
//PreProcess方法先于主程序执行 public override void PreProcess(objectsender, AspectEventArgs e)
{

}
//PostProcess方法后于主程序执行 public override void PostProcess(objectsender, AspectEventArgs e)
{
//把主程序的返回值改掉 e.MethodInfo.ReturnValue = $"共有校长2人";
}
}
}

运行,结果如下:

【控制台显示出】:
共有校长2人

到目前为止,我们已经知道了如何通过“Aspect横切面程序”修改主程序方法的参数值、返回值。

如果我们想进一步获取主程序的“命名空间”、“类名”、“方法名”、“参数名”、“参数类型”、“返回值类型”,则可以通过如下代码获取:

e.MethodInfo.NamespaceName                       //命名空间
e.MethodInfo.ClassName                           //类名
e.MethodInfo.MethodName                          //方法名
e.MethodInfo.ParameterInfos[0].Name              //参数名(第一个参数)
e.MethodInfo.ParameterInfos[0].ParameterType     //参数类型(第一个参数)
e.MethodInfo.ReturnValue.GetType()               //返回值类型

有时候,在某些特殊情况下,我们希望主程序方法不运行,此时则可以通过在PreProcess方法里把e.Continue设置为false来完成。

接前面的“第一个AOP程序”,比如:我们希望当人数大于10000时,主程序方法就不再运行,则可以通过把Interceptor1类的代码修改为如下样式来实现:

//从NuGet引用DeveloperSharp包
usingDeveloperSharp.Structure.Model.Aspect;namespaceSchool.Aspect
{
//横切面程序必须继承自AspectModel类 public classInterceptor1 : AspectModel
{
//PreProcess方法先于主程序执行 public override void PreProcess(objectsender, AspectEventArgs e)
{
//当人数大于10000时,主程序方法就不再运行 if (Convert.ToInt32(e.MethodInfo.ParameterValues[1]) > 10000)
e.Continue
= false;
}
//PostProcess方法后于主程序执行 public override void PostProcess(objectsender, AspectEventArgs e)
{

}
}
}

现在的这个示例是一个Aspect横切面程序拦截一个主程序。在后续将要讲解的“多个Aspect横切面程序拦截一个主程序”的情况中,只要有一个e.Continue=false被设置,主程序方法就不会运行(在此事先提点)。

3.一个横切面程序拦截多个主程序

为了演示这部分的内容,我们首先在前面“第一个AOP程序”的基础上,把主程序进行扩充。采取的动作是:

(1)在PersonManage类中增加一个GetInfo2方法

(2)再新增一个主程序类SystemManage,该类中有一个名为GetMessage1的方法。代码如下:

PersonManage类:

//从NuGet引用DeveloperSharp包
usingDeveloperSharp.Structure.Model;namespaceSchool.Logic
{
//主程序必须继承自LogicLayer类 public classPersonManage : LogicLayer
{
public string GetInfo1(string Name, intNum)
{
return $"共有{Name}{Num}人";
}
public string GetInfo2(string Name, intNum)
{
return $"学校共有{Name}{Num}人";
}
}
}

SystemManage类:

//从NuGet引用DeveloperSharp包
usingDeveloperSharp.Structure.Model;namespaceSchool.Logic
{
//主程序必须继承自LogicLayer类 public classSystemManage : LogicLayer
{
public string GetMessage1(string Name1, int Num1, string Name2, intNum2)
{
return $"第一组共有{Name1}{Num1}人,第二组共有{Name2}{Num2}人";
}
}
}

如此一来,现在就有了3个主程序方法。

接下来,我们修改配置文件,让Interceptor1去拦截上述的3个主程序方法。

若是在.Net Core环境下,DeveloperSharp.json文件的内容修改为如下:

{"DeveloperSharp":
{
"AspectObject":
[
{
"name":"School.Aspect.Interceptor1","scope":"School.Logic.PersonManage","method":"*" //星号*代表该作用域下的全部方法 },
{
"name":"School.Aspect.Interceptor1","scope":"School.Logic.SystemManage","method":"GetMessage1"}
]
}
}

若是在.Net Framework环境下,DeveloperSharp.xml文件的内容修改为如下:

<?xml version="1.0" encoding="utf-8"?>
<DeveloperSharp>
  <AspectObject>
    <Aoname="School.Aspect.Interceptor1"scope="School.Logic.PersonManage"method="*"/>
    <Aoname="School.Aspect.Interceptor1"scope="School.Logic.SystemManage"method="GetMessage1"/>
  </AspectObject>
</DeveloperSharp>

最后,我们把控制台启动程序修改为如下:

        //需要引用School.Aspect、School.Logic、DeveloperSharp三项
        static void Main(string[] args)
{
var pm = newSchool.Logic.PersonManage();var sm = newSchool.Logic.SystemManage();//要用这种形式调用主程序中的方法,AOP功能才会生效 var str1 = pm.InvokeMethod("GetInfo1", "学生", 200);var str2 = pm.InvokeMethod("GetInfo2", "学生", 200);var str3 = sm.InvokeMethod("GetMessage1", "学生", 200, "院士", 10);
Console.WriteLine(str1);
Console.WriteLine(str2);
Console.WriteLine(str3);

Console.ReadLine();
}

运行结果如下:

【控制台显示出】:

共有老师20人
学校共有老师20人
第一组共有老师20人,第二组共有院士10人

可见AOP所有拦截均已成功!

4.多个横切面程序拦截一个主程序

为了演示这部分的内容,我们还是要先回到前面的“第一个AOP程序”,在它的基础上,我们新增一个名为Interceptor2的Aspect横切面类,代码如下:

//从NuGet引用DeveloperSharp包
usingDeveloperSharp.Structure.Model.Aspect;namespaceSchool.Aspect
{
//横切面程序必须继承自AspectModel类 public classInterceptor2 : AspectModel
{
//PreProcess方法先于主程序执行 public override void PreProcess(objectsender, AspectEventArgs e)
{
//把主程序的两个参数值改掉 e.MethodInfo.ParameterValues[0] = "辅导员";
e.MethodInfo.ParameterValues[
1] = 40;
}
//PostProcess方法后于主程序执行 public override void PostProcess(objectsender, AspectEventArgs e)
{

}
}
}

如此一来,我们就有了2个Aspect横切面程序Interceptor1与Interceptor2。

接下来,我们修改配置文件,让Interceptor1、Interceptor2都去拦截主程序方法GetInfo1。

若是在.Net Core环境下,DeveloperSharp.json文件的内容修改为如下:

{"DeveloperSharp":
{
"AspectObject":
[
{
"name":"School.Aspect.Interceptor1","scope":"School.Logic.PersonManage","method":"GetInfo1"},
{
"name":"School.Aspect.Interceptor2","scope":"School.Logic.PersonManage","method":"GetInfo1"}
]
}
}

若是在.Net Framework环境下,DeveloperSharp.xml文件的内容修改为如下:

<?xml version="1.0" encoding="utf-8"?>
<DeveloperSharp>
  <AspectObject>
    <Aoname="School.Aspect.Interceptor1"scope="School.Logic.PersonManage"method="GetInfo1"/>
    <Aoname="School.Aspect.Interceptor2"scope="School.Logic.PersonManage"method="GetInfo1"/>
  </AspectObject>
</DeveloperSharp>

上述修改完毕,运行控制台主调程序,结果如下:

【控制台显示出】:
共有辅导员40人

从上述运行结果,我们大致可以推断出:Interceptor1、Interceptor2这两个Aspect横切面拦截器是按配置顺序执行的。其中,Interceptor1先把GetInfo1方法的两个参数值改为了("老师",20),接着,Interceptor2又把GetInfo1方法的两个参数值改为了("辅导员",40),所以最终GetInfo1方法的参数值变为了("辅导员",40)。

5.优势总结

本文所讲述的,是全网唯一实现AOP彻底解耦的技术方案。使用它,当你需要给主程序增加Aspect横切面拦截器时,无论是增加一个还是多个,
都不再需要修改&重新编译主程序
。这实现了不同功能构件之间的0依赖拼装/拆卸开发方式,随之而来的也会对研发团队的管理模式产生重大影响,意义深远...

6.展望

AOP对于程序代码的解耦、业务模块的拆分与拼装组合,有着巨大的作用。正确的使用AOP,甚至能对传统的软件架构设计,产生颠覆性的影响,如超级战士出场一般,让所有人刮目相看,完全耳目一新!!

为了让读者能直观感知AOP的上述神奇魅力,下面给出一个业务案例:
有一批货品要录入数据库,货品包含长、宽、高、颜色、类型等属性。现在有业务需求如下,
(1)当货品长度大于10厘米时,它在数据库中标记为A类;当货品长度大于20厘米时,标记为B类。
(2)当货品颜色无法分辨时,统一在数据库中默认标记为白色。
(3)每个货品录入数据库后,还要在另一个财务数据库中录入该货品的价格信息,同时把该货品的操作员名字记入日志文件。

这样的一个业务案例,你以前会怎么设计这个程序?今天学了AOP后你又会怎么设计程序?你会创建几个Aspect横切面了...?


原文首发于下方公众号,请关注!

向大佬学习,探行业内幕,享时代机遇。