首页 > 其他分享 >ABP微服务系列学习-搭建自己的微服务结构(三)

ABP微服务系列学习-搭建自己的微服务结构(三)

时间:2023-03-01 09:57:52浏览次数:44  
标签:服务 app Microsoft ABP context 系列学习 using options localhost

上一篇我们基础服务初步搭建完毕,接下来我们整一下认证和网关。

搭建认证服务

认证服务的话,ABP CLI生成的所有模板都包括了一个AuthServer。我们直接生成模板然后微调一下就可以直接用了。

abp new FunShow -t app --tiered

使用命令创建模板后,我们可以找到一个AuthServer。把项目移动到Apps目录下,然后我们开始改造一下这个项目。
首先修改项目文件的引用配置
修改EFCore项目引用为AdministrationService.EntityFrameworkCore和IdentityService.EntityFrameworkCore,
然后添加Shared.Localization和Shared.Hosting.AspNetCore项目引用,别的基本不用怎么修改,完整项目配置为:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <Import Project="..\..\..\..\common.props" />

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <UserSecretsId>b83bc18b-a6ca-4e2d-a827-26ffaff35dce</UserSecretsId>
    <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
    <DockerfileContext>..\..\..\..</DockerfileContext>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="6.0.5" />
    <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.0" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Volo.Abp.Caching.StackExchangeRedis" Version="7.0.0" />
    <PackageReference Include="Volo.Abp.EventBus.RabbitMQ" Version="7.0.0" />
    <PackageReference Include="Volo.Abp.BackgroundJobs.RabbitMQ" Version="7.0.0" />
    <PackageReference Include="Volo.Abp.Account.Web.OpenIddict" Version="7.0.0" />
    <PackageReference Include="Volo.Abp.Account.Application" Version="7.0.0" />
    <PackageReference Include="Volo.Abp.Account.HttpApi" Version="7.0.0" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\..\..\services\administration\src\FunShow.AdministrationService.EntityFrameworkCore\FunShow.AdministrationService.EntityFrameworkCore.csproj" />
    <ProjectReference Include="..\..\..\..\services\identity\src\FunShow.IdentityService.EntityFrameworkCore\FunShow.IdentityService.EntityFrameworkCore.csproj" />
    <ProjectReference Include="..\..\..\..\shared\FunShow.Shared.Hosting.AspNetCore\FunShow.Shared.Hosting.AspNetCore.csproj" />
    <ProjectReference Include="..\..\..\..\shared\FunShow.Shared.Localization\FunShow.Shared.Localization.csproj" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite" Version="2.0.0-*" />
  </ItemGroup>

  <ItemGroup>
    <Compile Remove="Logs\**" />
    <Content Remove="Logs\**" />
    <EmbeddedResource Remove="Logs\**" />
    <None Remove="Logs\**" />
  </ItemGroup>

</Project>

然后修改Program文件,主要是日志配置修改一下,别的不用改动

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using FunShow.Shared.Hosting.AspNetCore;
using Serilog;

namespace FunShow.AuthServer;

public class Program
{
    public async static Task<int> Main(string[] args)
    {
        var assemblyName = typeof(Program).Assembly.GetName().Name;

        SerilogConfigurationHelper.Configure(assemblyName);

        try
        {
            Log.Information($"Starting {assemblyName}.");
            var builder = WebApplication.CreateBuilder(args);
            builder.Host
                .AddAppSettingsSecretsJson()
                .UseAutofac()
                .UseSerilog();
            await builder.AddApplicationAsync<FunShowAuthServerModule>();
            var app = builder.Build();
            await app.InitializeApplicationAsync();
            await app.RunAsync();
            return 0;
        }
        catch (Exception ex)
        {
            Log.Fatal(ex, $"{assemblyName} terminated unexpectedly!");
            return 1;
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }
}

修改module.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using FunShow.AdministrationService.EntityFrameworkCore;
using FunShow.IdentityService.EntityFrameworkCore;
using FunShow.Shared.Hosting.AspNetCore;
using Prometheus;
using StackExchange.Redis;
using Volo.Abp;
using Volo.Abp.Account;
using Volo.Abp.Account.Web;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.Auditing;
using Volo.Abp.BackgroundJobs.RabbitMQ;
using Volo.Abp.Caching;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.Emailing;
using Volo.Abp.EventBus.RabbitMq;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.OpenIddict;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.VirtualFileSystem;
using Microsoft.AspNetCore.HttpOverrides;
using FunShow.Shared.Localization;

namespace FunShow.AuthServer;

[DependsOn(
    typeof(AbpCachingStackExchangeRedisModule),
    typeof(AbpEventBusRabbitMqModule),
    typeof(AbpBackgroundJobsRabbitMqModule),
    typeof(AbpAspNetCoreMvcUiLeptonXLiteThemeModule),
    typeof(AbpAccountWebOpenIddictModule),
    typeof(AbpAccountApplicationModule),
    typeof(AbpAccountHttpApiModule),
    typeof(AdministrationServiceEntityFrameworkCoreModule),
    typeof(IdentityServiceEntityFrameworkCoreModule),
    typeof(FunShowSharedHostingAspNetCoreModule),
    typeof(FunShowSharedLocalizationModule)
)]
public class FunShowAuthServerModule : AbpModule
{
    public override void PreConfigureServices(ServiceConfigurationContext context)
    {
        var hostingEnvironment = context.Services.GetHostingEnvironment();
        var configuration = context.Services.GetConfiguration();
    
    PreConfigure<OpenIddictBuilder>(builder =>
    {
    	builder.AddValidation(options =>
    {
    options.AddAudiences("AccountService");
    options.UseLocalServer();
    options.UseAspNetCore();
    });
    });
    if (!hostingEnvironment.IsDevelopment())
    {
    	PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
    	{
    	options.AddDevelopmentEncryptionAndSigningCertificate = false;
    	});
    
        PreConfigure<OpenIddictServerBuilder>(builder =>
        {
            builder.AddSigningCertificate(GetSigningCertificate(hostingEnvironment, configuration));
            builder.AddEncryptionCertificate(GetSigningCertificate(hostingEnvironment, configuration));
            });
        }
    }
    
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
    //You can disable this setting in production to avoid any potential security risks.
        Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
        
        var hostingEnvironment = context.Services.GetHostingEnvironment();
        var configuration = context.Services.GetConfiguration();
        
        ConfigureBundles();
        ConfigureSwagger(context, configuration);
        ConfigureSameSiteCookiePolicy(context);
        ConfigureExternalProviders(context);
        
        Configure<AbpMultiTenancyOptions>(options =>
        {
        	options.IsEnabled = true;
        });
        
        Configure<AbpAuditingOptions>(options =>
        {
            options.ApplicationName = "AuthServer";
        });
        
        Configure<AppUrlOptions>(options =>
        {
            options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];
            options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(','));
        });
        
        Configure<AbpDistributedCacheOptions>(options =>
        {
        	options.KeyPrefix = "FunShow:";
        });
        
        var dataProtectionBuilder = context.Services.AddDataProtection().SetApplicationName("FunShow");
        var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
        dataProtectionBuilder.PersistKeysToStackExchangeRedis(redis, "FunShow-Protection-Keys");
        
        context.Services.AddCors(options =>
        {
        	options.AddDefaultPolicy(builder =>
            {
            builder
            .WithOrigins(
            configuration["App:CorsOrigins"]
            .Split(",", StringSplitOptions.RemoveEmptyEntries)
            .Select(o => o.Trim().RemovePostFix("/"))
            .ToArray()
            )
            .WithAbpExposedHeaders()
            .SetIsOriginAllowedToAllowWildcardSubdomains()
            .AllowAnyHeader()
            .AllowAnyMethod()
            .AllowCredentials();
            });
        });
        
        #if DEBUG
        context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>());
        #endif
        
        if (hostingEnvironment.IsDevelopment())
        {
            Configure<AbpVirtualFileSystemOptions>(options =>
            {
            options.FileSets.ReplaceEmbeddedByPhysical<FunShowSharedLocalizationModule>(Path.Combine(
            hostingEnvironment.ContentRootPath,
            $"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}shared{Path.DirectorySeparatorChar}FunShow.Shared.Localization"));
            });
        }
    }
    
    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var app = context.GetApplicationBuilder();
        var env = context.GetEnvironment();
        
        var configuration = context.ServiceProvider.GetRequiredService<IConfiguration>();
        
        if (env.IsDevelopment())
        {
        	app.UseDeveloperExceptionPage();
        }
    
        app.UseAbpRequestLocalization();
        
        if (!env.IsDevelopment())
        {
        	app.UseErrorPage();
        }
        var forwardOptions = new ForwardedHeadersOptions
        {
            ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto,
            RequireHeaderSymmetry = false
        };
    
        forwardOptions.KnownNetworks.Clear();
        forwardOptions.KnownProxies.Clear();
        
    // ref: https://github.com/aspnet/Docs/issues/2384
        app.UseForwardedHeaders(forwardOptions);
        
        app.UseCorrelationId();
        app.UseAbpSecurityHeaders();
        app.UseStaticFiles();
        app.UseRouting();
        app.UseCors();
        app.UseCookiePolicy();
        app.UseHttpMetrics();
        app.UseAuthentication();
        app.UseAbpOpenIddictValidation();
        app.UseAbpSerilogEnrichers();
        app.UseUnitOfWork();
        app.UseAuthorization();
        app.UseSwagger();
        app.UseAbpSwaggerUI(options =>
        {
            options.SwaggerEndpoint("/swagger/v1/swagger.json", "Account Service API");
            options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
        });
        	app.UseAuditing();
        	app.UseConfiguredEndpoints(endpoints =>
            {
            	endpoints.MapMetrics();
            });
    }
    
    private void ConfigureBundles()
    {
        Configure<AbpBundlingOptions>(options =>
        {
            options.StyleBundles.Configure(
            LeptonXLiteThemeBundles.Styles.Global,
            bundle =>
            {
            	bundle.AddFiles("/global-styles.css");
            }
        );
        });
    }
    
    private void ConfigureExternalProviders(ServiceConfigurationContext context)
    {
    	context.Services.AddAuthentication();
    }
    
    private X509Certificate2 GetSigningCertificate(IWebHostEnvironment hostingEnv, IConfiguration configuration)
    {
        var fileName = "authserver.pfx";
        var passPhrase = "2D7AA457-5D33-48D6-936F-C48E5EF468ED";
        var file = Path.Combine(hostingEnv.ContentRootPath, fileName);
        
        if (!File.Exists(file))
        {
        	throw new FileNotFoundException($"Signing Certificate couldn't found: {file}");
        }
    
    	return new X509Certificate2(file, passPhrase);
    }
    
    private void ConfigureSwagger(ServiceConfigurationContext context, IConfiguration configuration)
    {
        SwaggerConfigurationHelper.ConfigureWithAuth(
        context: context,
        authority: configuration["AuthServer:Authority"],
        scopes: new Dictionary<string, string> {
        /* Requested scopes for authorization code request and descriptions for swagger UI only */
        { "AccountService", "Account Service API" }
        },
        apiTitle: "Account Service API"
        );
    }
    
    private void ConfigureSameSiteCookiePolicy(ServiceConfigurationContext context)
    {
    	context.Services.AddSameSiteCookiePolicy();
    }
}

最后修改配置文件

{
  "App": {
    "SelfUrl": "https://localhost:44322",
    "CorsOrigins": "http://localhost:4200,http://localhost:9527,https://localhost:44307,https://localhost:44325,https://localhost:44353,https://localhost:44367,https://localhost:44388,https://localhost:44381,https://localhost:44361",
    "RedirectAllowedUrls": "http://localhost:4200,https://localhost:44307,https://localhost:44321,http://localhost:9527"
  },
  "AuthServer": {
    "Authority": "https://localhost:44322",
    "RequireHttpsMetadata": "true",
    "SwaggerClientId": "WebGateway_Swagger"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "AdministrationService": "Host=localhost;Port=5432;User ID=postgres;password=myPassw0rd;Pooling=true;Database=FunShow_Administration;",
    "IdentityService": "Host=localhost;Port=5432;User ID=postgres;password=myPassw0rd;Pooling=true;Database=FunShow_Identity;"
  },
  "StringEncryption": {
    "DefaultPassPhrase": "fCrJICTG3WoyissG"
  },
  "Redis": {
    "Configuration": "localhost:6379"
  },
  "RabbitMQ": {
    "Connections": {
      "Default": {
        "HostName": "localhost"
      }
    },
    "EventBus": {
      "ClientName": "FunShow_AuthServer",
      "ExchangeName": "FunShow"
    }
  },
  "ElasticSearch": {
    "Url": "http://localhost:9200"
  }
}

这样我们认证服务即修改完成。

搭建网关服务

网关服务我们直接新建一个空asp.net core项目。
然后只需要添加一个我们的Shared.Hosting.Gateways项目引用即可。

<Project Sdk="Microsoft.NET.Sdk.Web">

  <Import Project="..\..\..\..\common.props" />

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\..\..\shared\FunShow.Shared.Hosting.Gateways\FunShow.Shared.Hosting.Gateways.csproj" />
  </ItemGroup>

  <ItemGroup>
    <Compile Remove="Logs\**" />
    <Content Remove="Logs\**" />
    <EmbeddedResource Remove="Logs\**" />
    <None Remove="Logs\**" />
  </ItemGroup>

</Project>

修改Program.cs

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using FunShow.Shared.Hosting.AspNetCore;
using Serilog;

namespace FunShow.WebGateway;

public class Program
{
    public async static Task<int> Main(string[] args)
    {
        var assemblyName = typeof(Program).Assembly.GetName().Name;

        SerilogConfigurationHelper.Configure(assemblyName);

        try
        {
            Log.Information($"Starting {assemblyName}.");
            var builder = WebApplication.CreateBuilder(args);
            builder.Host
                .AddAppSettingsSecretsJson()
                .AddYarpJson()
                .UseAutofac()
                .UseSerilog();
            await builder.AddApplicationAsync<FunShowWebGatewayModule>();
            var app = builder.Build();
            await app.InitializeApplicationAsync();
            await app.RunAsync();
            return 0;
        }
        catch (Exception ex)
        {
            Log.Fatal(ex, $"{assemblyName} terminated unexpectedly!");
            return 1;
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }
}

这里和认证服务基本一致,就是多了一个AddYarpJson()来添加我们的yarp的配置文件。
在目录下新建yarp.json文件,添加我们的yarp配置内容。配置集群和路由如下:

{
  "ReverseProxy": {
    "Routes": {
      "Account Service": {
        "ClusterId": "accountCluster",
        "Match": {
          "Path": "/api/account/{**everything}"
        }
      },
      "Identity Service": {
        "ClusterId": "identityCluster",
        "Match": {
          "Path": "/api/identity/{**everything}"
        }
      },
      "Administration Service": {
        "ClusterId": "administrationCluster",
        "Match": {
          "Path": "/api/abp/{**everything}"
        }
      },
      "Logging Service": {
        "ClusterId": "loggingCluster",
        "Match": {
          "Path": "/api/LoggingService/{**everything}"
        }
      },
      "feature-management-route": {
        "ClusterId": "feature-management-cluster",
        "Match": {
          "Path": "/api/feature-management/{**everything}"
        }
      },
      "permission-management-route": {
        "ClusterId": "permission-management-cluster",
        "Match": {
          "Path": "/api/permission-management/{**everything}"
        }
      },
      "setting-management-route": {
        "ClusterId": "setting-management-cluster",
        "Match": {
          "Path": "/api/setting-management/{**everything}"
        }
      }
    },
    "Clusters": {
      "accountCluster": {
        "Destinations": {
          "destination1": {
            "Address": "https://localhost:44322"
          }
        }
      },
      "identityCluster": {
        "Destinations": {
          "destination1": {
            "Address": "https://localhost:44388"
          }
        }
      },
      "administrationCluster": {
        "Destinations": {
          "destination1": {
            "Address": "https://localhost:44367"
          }
        }
      },
      "loggingCluster": {
        "Destinations": {
          "destination1": {
            "Address": "https://localhost:45124"
          }
        }
      },
      "feature-management-cluster": {
        "Destinations": {
          "destination1": {
            "Address": "https://localhost:44367"
          }
        }
      },
      "permission-management-cluster": {
        "Destinations": {
          "destination1": {
            "Address": "https://localhost:44367"
          }
        }
      },
      "setting-management-cluster": {
        "Destinations": {
          "destination1": {
            "Address": "https://localhost:44367"
          }
        }
      }
    }
  }
}

在appsettings.json文件添加我们认证服务的地址

{
  "App": {
    "SelfUrl": "https://localhost:44325",
    "CorsOrigins": "http://localhost:4200,https://localhost:44307,http://localhost:9527"
  },
  "AuthServer": {
    "Authority": "https://localhost:44322",
    "RequireHttpsMetadata": "true",
    "SwaggerClientId": "WebGateway_Swagger"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "Redis": {
    "Configuration": "localhost:6379"
  },
  "ElasticSearch": {
    "Url": "http://localhost:9200"
  }
}

最后我们添加FunShowWebGatewayModule文件。配置我们yarp的服务。

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Rewrite;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using FunShow.Shared.Hosting.AspNetCore;
using FunShow.Shared.Hosting.Gateways;
using Volo.Abp;
using Volo.Abp.Modularity;

namespace FunShow.WebGateway;

[DependsOn(
    typeof(FunShowSharedHostingGatewaysModule)
)]
public class FunShowWebGatewayModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        // Enable if you need hosting environment
        // var hostingEnvironment = context.Services.GetHostingEnvironment();
        var configuration = context.Services.GetConfiguration();
        var hostingEnvironment = context.Services.GetHostingEnvironment();
        
        SwaggerConfigurationHelper.ConfigureWithAuth(
            context: context,
            authority: configuration["AuthServer:Authority"],
            scopes: new
            Dictionary<string, string> /* Requested scopes for authorization code request and descriptions for swagger UI only */ {
            { "AccountService", "Account Service API" },
            { "IdentityService", "Identity Service API" },
            { "AdministrationService", "Administration Service API" },
            { "LoggingService", "Logging Service API" }
            },
            apiTitle: "Web Gateway API"
        );
        
        context.Services.AddCors(options =>
        {
        	options.AddDefaultPolicy(builder =>
        {
        builder
            .WithOrigins(
                configuration["App:CorsOrigins"]
                .Split(",", StringSplitOptions.RemoveEmptyEntries)
                .Select(o => o.Trim().RemovePostFix("/"))
                .ToArray()
            )
            .WithAbpExposedHeaders()
            .SetIsOriginAllowedToAllowWildcardSubdomains()
            .AllowAnyHeader()
            .AllowAnyMethod()
            .AllowCredentials();
            });
        });
    }

    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var app = context.GetApplicationBuilder();
        var env = context.GetEnvironment();
        
        if (env.IsDevelopment())
        {
        	app.UseDeveloperExceptionPage();
        }
        app.UseCorrelationId();
        app.UseAbpSerilogEnrichers();
        app.UseCors();
        app.UseSwaggerUIWithYarp(context);
        
        app.UseRewriter(new RewriteOptions()
            // Regex for "", "/" and "" (whitespace)
            .AddRedirect("^(|\\|\\s+)$", "/swagger"));
            
        app.UseRouting();
        app.UseEndpoints(endpoints =>
        {
        	endpoints.MapReverseProxy();
        });
    }
}

UseSwaggerUIWithYarp是从我们Yarp配置文件中读取服务信息去构造swagger路由配置。
好了,到这我们认证服务和网关服务也搭建完毕,下一篇我们开始迁移数据库。

标签:服务,app,Microsoft,ABP,context,系列学习,using,options,localhost
From: https://www.cnblogs.com/fanshaoO/p/17166952.html

相关文章

  • pgsql中pg_dump显示:因为服务器版本不匹配而终止
    1、输入pg_dump命令后,显示“因为服务器版本不匹配而终止”。如图所示:  2、因为pgsql安装的版本为11.6,而pg_dump的版本成为了9.2,故无法备份。3、找到pg_dump的位置。......
  • 服务器和电脑主机的区别?
    服务器主要应用于企业和个人的工作中,和家用的主机不同,服务器的任务是保证任何时候用户都能够通过终端顺利访问服务器,并传输和共享服务器中的数据。1.服务器最重要的并不是......
  • 微服务Spring Cloud Alibaba简单笔记
    NacosNacos体系架构领域模型Nacos领域模型描述了服务与实例之间的边界和层级关系。Nacos的服务领域模型是以“服务”为维度构建起来的,这个服务并不是指集群中的单个......
  • Windows平台Unity Camera场景实现轻量级RTSP服务和RTMP推送
    技术背景随着VR技术在医疗、军事、农业、学校、景区、消防、公共安全、研学机构、展厅展馆,商场等场所普及,开发者对Unity平台下的直播体验提出了更高的要求。技术实现Unity平......
  • 号码隐私保护服务:保障亿万消费者的隐私安全
    如何从繁杂的数据指标中精确识别出异常情况,是保障系统高可用的关键。一、引言近年来,针对电商的电信诈骗层出不穷。不法分子借助非法手段获取到消费者的个人信息,假冒电商......
  • Golang微服务(三)
    Golang微服务(三)目录Golang微服务(三)分布式锁互斥锁悲观锁乐观锁基于Redis的分布式锁分布式锁gRPCservice层demorouterhandler如下:/*>>>modeltypeInventorystruc......
  • 【Azure 存储服务】Azure Storage Account 下的 Table 查询的性能调优
    问题描述AzureStorageAccount下的Table查询的性能调优?问题解答因为AzureStorageTable服务(表服务)与常规的关系型数据库不一样(例如:MySQL,SQLServer等),他里面存储的......
  • 2-微服务技术选型
    微服务介绍单体架构单体架构就是将所有功能集成到一个项目中进行开发,打包成一个包进行部署。好处是架构简单,各个模块采用统一的架构方案,并且进行部署的时候只需要将一个......
  • 深入分析MobileAI图像超分最佳方案:ABPN
    前言 本文设计一种8-bit量化版高效网络并将其部署到移动端,旨在构建一种实时量化模型用于真实场景(比如实时视频超分)。本文转载自AIWalker来源|HappyAIWalker欢迎关......
  • 一步打通多渠道服务场景 中电金信源启移动开发平台MADP功能“上新”
    日前,中电金信源启移动开发平台MADP功能迭代升级,“上新”源启小程序开发平台。定位“为金融业定制”的移动PaaS平台,源启小程序开发平台为银行、互联网金融、保险、证券客户提......