通过lms.samples熟悉lms微服务框架的使用详解

经过一段时间的开发与测试,终于发布了Lms框架的第一个正式版本(1.0.0版本),并给出了lms框架的样例项目lms.samples。本文通过对lms.samples的介绍,简述如何通过lms框架快速的构建一个微服务的业务框架,并进行应用开发。

lms.samples项目基本介绍

lms.sample项目由三个独立的微服务应用模块组成:account、stock、order和一个网关项目gateway构成。

业务应用模块

每个独立的微服务应用采用模块化设计,主要由如下几部分组成:

  1. 主机(Host): 主要用于托管微服务应用本身,主机通过引用应用服务项目(应用接口的实现),托管微服务应用,通过托管应用服务,在主机启动的过程中,向服务注册中心注册服务路由。
  2. 应用接口层(Application.Contracts): 用于定义应用服务接口,通过应用接口,该微服务模块与其他微服务模块或是网关进行rpc通信的能力。在该项目中,除了定义应用服务接口之前,一般还定义与该应用接口相关的DTO对象。应用接口除了被该微服务应用项目引用,并实现应用服务之前,还可以被网关或是其他微服务模块引用。网关或是其他微服务项目通过应用接口生成的代理与该微服务模块通过rpc进行通信。
  3. 应用服务层(Application): 应用服务是该微服务定义的应用接口的实现。应用服务与DDD传统分层架构的应用层的概念一致。主要负责外部通信与领域层之间的协调。一般地,应用服务进行业务流程控制,但是不包含业务逻辑的实现。
  4. 领域层(Domain): 负责表达业务概念,业务状态信息以及业务规则,是该微服务模块的业务核心。一般地,在该层可以定义聚合根、实体、领域服务等对象。
  5. 领域共享层(Domain.Shared): 该层用于定义与领域对象相关的模型、实体等相关类型。不包含任何业务实现,可以被其他微服务引用。
  6. 数据访问(DataAccess)层: 该层一般用于封装数据访问相关的对象。例如:仓库对象、 SqlHelper、或是ORM相关的类型等。在lms.samples中,通过efcore实现数据的读写操作。

服务聚合与网关

lms框架不允许服务外部与微服务主机直接通信,应用请求必须通过http请求到达网关,网关通过lms提供的中间件解析到服务条目,并通过rpc与集群内部的微服务进行通信。所以,如果服务需要与集群外部进行通信,那么,开发者定义的网关必须要引用各个微服务模块的应用接口层;以及必须要使用lms相关的中间件。

开发环境

  1. .net版本: 5.0.101
  2. lms版本: 1.0.0
  3. IDE: (1) visual studio 最新版 (2) Rider(推荐)

主机与应用托管

主机的创建步骤

通过lms框架创建一个业务模块非常方便,只需要通过如下4个步骤,就可以轻松的创建一个lms应用业务模块。

1.创建项目

创建控制台应用(Console Application)项目,并且引用Silky.Lms.NormHost包。

dotnet add package Silky.Lms.NormHost --version 1.0.0

2.应用程序入口与主机构建

main方法中,通用.net的主机Host构建并注册lms微服务。在注册lms微服务时,需要指定lms启动的依赖模块。

一般地,如果开发者不需要额外依赖其他模块,也无需在应用启动或停止时执行方法,那么您可以直接指定NormHostModule模块。

 public class Program
    {
        public static async Task Main(string[] args)
        {
            await CreateHostBuilder(args).Build().RunAsync();
        }

        private static IHostBuilder CreateHostBuilder(string[] args)
        {
            return Host.CreateDefaultBuilder(args)
                    .RegisterLmsServices<NormHostModule>()
                ;
        }
    }

3.配置文件

lms框架支持yml或是json格式作为配置文件。通过appsettings.yml对lms框架进行统一配置,通过appsettings.${Environment}.yml对不同环境变量下的配置项进行设置。

开发者如果直接通过项目的方式启动应用,那么可以通过Properties/launchSettings.jsonenvironmentVariables.DOTNET_ENVIRONMENT环境变量。如果通过docker-compose的方式启动应用,那么可以通过.env设置DOTNET_ENVIRONMENT环境变量。

为保证配置文件有效,开发者需要显式的将配置文件拷贝到项目生成目录下。

4.引用应用服务层和数据访问层

一般地,主机项目需要引用该微服务模块的应用服务层和数据访问层。只有主机引用应用服务层,主机在启动时,才会生成服务条目的路由,并且将服务路由注册到服务注册中心。

一个典型的主机项目文件如下所示:

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

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net5.0</TargetFramework>
    </PropertyGroup>

    <ItemGroup>
      <PackageReference Include="Silky.Lms.NormHost" Version="$(LmsVersion)" />
    </ItemGroup>

    <ItemGroup>
      <None Update="appsettings.yml">
        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      </None>
      <None Update="appsettings.Production.yml">
        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      </None>
      <None Update="appsettings.Development.yml">
        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      </None>
    </ItemGroup>

    <ItemGroup>
      <ProjectReference Include="..\Lms.Account.Application\Lms.Account.Application.csproj" />
      <ProjectReference Include="..\Lms.Account.EntityFrameworkCore\Lms.Account.EntityFrameworkCore.csproj" />
    </ItemGroup>
</Project>

配置

一般地,一个微服务模块的主机必须要配置:服务注册中心、分布式锁链接、分布式缓存地址、集群rpc通信token、数据库链接地址等。

如果使用docker-compose来启动和调试应用的话,那么,rpc配置节点下的的host和port可以缺省,因为生成的每个容器的都有自己的地址和端口号。

如果直接通过项目的方式启动和调试应用的话,那么,必须要配置rpc节点下的port,每个微服务模块的主机应用有自己的端口号。

lms框架的必要配置如下所示:

rpc:
  host: 0.0.0.0
  rpcPort: 2201
  token: ypjdYOzNd4FwENJiEARMLWwK0v7QUHPW
registrycenter:
  connectionStrings: 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183;127.0.0.1:2184,127.0.0.1:2185,127.0.0.1:2186 # 使用分号;来区分不同的服务注册中心
  registryCenterType: Zookeeper
distributedCache:
  redis:
    isEnabled: true
    configuration: 127.0.0.1:6379,defaultDatabase=0
lock:
  lockRedisConnection: 127.0.0.1:6379,defaultDatabase=1
connectionStrings:
    default: server=127.0.0.1;port=3306;database=account;uid=root;pwd=qwe!P4ss;

应用接口

应用接口定义

一般地,在应用接口层开发者需要安装Silky.Lms.Rpc包。如果该微服务模块还涉及到分布式事务,那么还需要安装Silky.Lms.Transaction.Tcc,当然,您也可以选择在应用接口层安装Silky.Lms.Transaction包,在应用服务层安装Silky.Lms.Transaction.Tcc包。

  1. 开发者只需要在应用接口通过ServiceRouteAttribute特性对应用接口进行直接即可。
  2. Lms约定应用接口应当以IXxxAppService命名,这样,服务条目生成的路由则会以api/xxx形式生成。当然这并不是强制的。
  3. 每个应用接口的方法都对应着一个服务条目,服务条目的Id为: 方法的完全限定名 + 参数名
  4. 您可以在应用接口层对方法的缓存、路由、服务治理、分布式事务进行相关配置。该部分内容请参考官方文档
  5. 网关或是其他模块的微服务项目需要引用服务应用接口项目或是通过nuget的方式安装服务应用接口生成的包。
  6. [Governance(ProhibitExtranet = true)]可以标识一个方法禁止与集群外部进行通信,通过网关也不会生成swagger文档。
  7. 应用接口方法生成的WebApi支持restful API风格。Lms支持通过方法的约定命名生成对应http方法请求的WebApi。您当然开发者也可以通过HttpMethodAttribute特性对某个方法进行注解。

一个典型的应用接口的定义

/// <summary>
    /// 账号服务
    /// </summary>
    [ServiceRoute]
    public interface IAccountAppService
    {
        /// <summary>
        /// 新增账号
        /// </summary>
        /// <param name="input">账号信息</param>
        /// <returns></returns>
        Task<GetAccountOutput> Create(CreateAccountInput input);

        /// <summary>
        /// 通过账号名称获取账号
        /// </summary>
        /// <param name="name">账号名称</param>
        /// <returns></returns>
        [GetCachingIntercept("Account:Name:{0}")]
        [HttpGet("{name:string}")]
        Task<GetAccountOutput> GetAccountByName([CacheKey(0)] string name);

        /// <summary>
        /// 通过Id获取账号信息
        /// </summary>
        /// <param name="id">账号Id</param>
        /// <returns></returns>
        [GetCachingIntercept("Account:Id:{0}")]
        [HttpGet("{id:long}")]
        Task<GetAccountOutput> GetAccountById([CacheKey(0)] long id);

        /// <summary>
        /// 更新账号信息
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [UpdateCachingIntercept( "Account:Id:{0}")]
        Task<GetAccountOutput> Update(UpdateAccountInput input);

        /// <summary>
        /// 删除账号信息
        /// </summary>
        /// <param name="id">账号Id</param>
        /// <returns></returns>
        [RemoveCachingIntercept("GetAccountOutput","Account:Id:{0}")]
        [HttpDelete("{id:long}")]
        Task Delete([CacheKey(0)]long id);

        /// <summary>
        /// 订单扣款
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [Governance(ProhibitExtranet = true)]
        [RemoveCachingIntercept("GetAccountOutput","Account:Id:{0}")]
        [Transaction]
        Task<long?> DeductBalance(DeductBalanceInput input);
    }

应用服务--应用接口的实现

  1. 应用服务层只需要引用应用服务接口层以及领域服务层,并实现应用接口相关的方法。
  2. 确保该微服务模块的主机引用了该模块的应用服务层,这样主机才能够托管该应用本身。
  3. 应用服务层可以通过引用其他微服务模块的应用接口层项目(或是安装nuget包,取决于开发团队的项目管理方法),与其他微服务模块进行rpc通信。
  4. 应用服务层需要依赖领域服务,通过调用领域服务的相关接口,实现该模块的核心业务逻辑。
  5. DTO到实体对象或是实体对DTO对象的映射关系可以在该层指定映射关系。

一个典型的应用服务的实现如下所示:

public class AccountAppService : IAccountAppService
    {
        private readonly IAccountDomainService _accountDomainService;

        public AccountAppService(IAccountDomainService accountDomainService)
        {
            _accountDomainService = accountDomainService;
        }

        public async Task<GetAccountOutput> Create(CreateAccountInput input)
        {
            var account = input.MapTo<Domain.Accounts.Account>();
            account = await _accountDomainService.Create(account);
            return account.MapTo<GetAccountOutput>();
        }

        public async Task<GetAccountOutput> GetAccountByName(string name)
        {
            var account = await _accountDomainService.GetAccountByName(name);
            return account.MapTo<GetAccountOutput>();
        }

        public async Task<GetAccountOutput> GetAccountById(long id)
        {
            var account = await _accountDomainService.GetAccountById(id);
            return account.MapTo<GetAccountOutput>();
        }

        public async Task<GetAccountOutput> Update(UpdateAccountInput input)
        {
            var account = await _accountDomainService.Update(input);
            return account.MapTo<GetAccountOutput>();
        }

        public Task Delete(long id)
        {
            return _accountDomainService.Delete(id);
        }

        [TccTransaction(ConfirmMethod = "DeductBalanceConfirm", CancelMethod = "DeductBalanceCancel")]
        public async Task<long?> DeductBalance(DeductBalanceInput input)
        {
            var account = await _accountDomainService.GetAccountById(input.AccountId);
            if (input.OrderBalance > account.Balance)
            {
                throw new BusinessException("账号余额不足");
            }
            return await _accountDomainService.DeductBalance(input, TccMethodType.Try);
        }

        public Task DeductBalanceConfirm(DeductBalanceInput input)
        {
            return _accountDomainService.DeductBalance(input, TccMethodType.Confirm);
        }

        public Task DeductBalanceCancel(DeductBalanceInput input)
        {
            return _accountDomainService.DeductBalance(input, TccMethodType.Cancel);
        }
    }

领域层--微服务的核心业务实现

  1. 领域层是该微服务模块核心业务处理的模块,一般用于定于聚合根、实体、领域服务、仓储等业务对象。
  2. 领域层引用该微服务模块的应用接口层,方便使用dto对象。
  3. 领域层可以通过引用其他微服务模块的应用接口层项目(或是安装nuget包,取决于开发团队的项目管理方法),与其他微服务模块进行rpc通信。
  4. 领域服务必须要直接或间接继承ITransientDependency接口,这样,该领域服务才会被注入到ioc容器。
  5. lms.samples 项目使用TanvirArjel.EFCore.GenericRepository包实现数据的读写操作。

一个典型的领域服务的实现如下所示:

public class AccountDomainService : IAccountDomainService
    {
        private readonly IRepository _repository;
        private readonly IDistributedCache<GetAccountOutput, string> _accountCache;

        public AccountDomainService(IRepository repository,
            IDistributedCache<GetAccountOutput, string> accountCache)
        {
            _repository = repository;
            _accountCache = accountCache;
        }

        public async Task<Account> Create(Account account)
        {
            var exsitAccountCount = await _repository.GetCountAsync<Account>(p => p.Name == account.Name);
            if (exsitAccountCount > 0)
            {
                throw new BusinessException($"已经存在{account.Name}名称的账号");
            }

            exsitAccountCount = await _repository.GetCountAsync<Account>(p => p.Email == account.Email);
            if (exsitAccountCount > 0)
            {
                throw new BusinessException($"已经存在{account.Email}Email的账号");
            }

            await _repository.InsertAsync<Account>(account);
            return account;
        }

        public async Task<Account> GetAccountByName(string name)
        {
            var accountEntry = _repository.GetQueryable<Account>().FirstOrDefault(p => p.Name == name);
            if (accountEntry == null)
            {
                throw new BusinessException($"不存在名称为{name}的账号");
            }

            return accountEntry;
        }

        public async Task<Account> GetAccountById(long id)
        {
            var accountEntry = _repository.GetQueryable<Account>().FirstOrDefault(p => p.Id == id);
            if (accountEntry == null)
            {
                throw new BusinessException($"不存在Id为{id}的账号");
            }

            return accountEntry;
        }

        public async Task<Account> Update(UpdateAccountInput input)
        {
            var account = await GetAccountById(input.Id);
            if (!account.Email.Equals(input.Email))
            {
                var exsitAccountCount = await _repository.GetCountAsync<Account>(p => p.Email == input.Email);
                if (exsitAccountCount > 0)
                {
                    throw new BusinessException($"系统中已经存在Email为{input.Email}的账号");
                }
            }

            if (!account.Name.Equals(input.Name))
            {
                var exsitAccountCount = await _repository.GetCountAsync<Account>(p => p.Name == input.Name);
                if (exsitAccountCount > 0)
                {
                    throw new BusinessException($"系统中已经存在Name为{input.Name}的账号");
                }
            }

            await _accountCache.RemoveAsync($"Account:Name:{account.Name}");
            account = input.MapTo(account);
            await _repository.UpdateAsync(account);
            return account;
        }

        public async Task Delete(long id)
        {
            var account = await GetAccountById(id);
            await _accountCache.RemoveAsync($"Account:Name:{account.Name}");
            await _repository.DeleteAsync(account);
        }

        public async Task<long?> DeductBalance(DeductBalanceInput input, TccMethodType tccMethodType)
        {
            var account = await GetAccountById(input.AccountId);
            var trans = await _repository.BeginTransactionAsync();
            BalanceRecord balanceRecord = null;
            switch (tccMethodType)
            {
                case TccMethodType.Try:
                    account.Balance -= input.OrderBalance;
                    account.LockBalance += input.OrderBalance;
                    balanceRecord = new BalanceRecord()
                    {
                        OrderBalance = input.OrderBalance,
                        OrderId = input.OrderId,
                        PayStatus = PayStatus.NoPay
                    };
                    await _repository.InsertAsync(balanceRecord);
                    RpcContext.GetContext().SetAttachment("balanceRecordId",balanceRecord.Id);
                    break;
                case TccMethodType.Confirm:
                    account.LockBalance -= input.OrderBalance;
                    var balanceRecordId1 = RpcContext.GetContext().GetAttachment("orderBalanceId")?.To<long>();
                    if (balanceRecordId1.HasValue)
                    {
                        balanceRecord = await _repository.GetByIdAsync<BalanceRecord>(balanceRecordId1.Value);
                        balanceRecord.PayStatus = PayStatus.Payed;
                        await _repository.UpdateAsync(balanceRecord);
                    }
                    break;
                case TccMethodType.Cancel:
                    account.Balance += input.OrderBalance;
                    account.LockBalance -= input.OrderBalance;
                    var balanceRecordId2 = RpcContext.GetContext().GetAttachment("orderBalanceId")?.To<long>();
                    if (balanceRecordId2.HasValue)
                    {
                        balanceRecord = await _repository.GetByIdAsync<BalanceRecord>(balanceRecordId2.Value);
                        balanceRecord.PayStatus = PayStatus.Cancel;
                        await _repository.UpdateAsync(balanceRecord);
                    }
                    break;
            }

            await _repository.UpdateAsync(account);
            await trans.CommitAsync();
            await _accountCache.RemoveAsync($"Account:Name:{account.Name}");
            return balanceRecord?.Id;
        }
    }

数据访问(EntityFrameworkCore)--通过efcore实现数据读写

  • lms.samples项目使用orm框架efcore进行数据读写。
  • lms提供了IConfigureService,通过继承该接口即可使用IServiceCollection的实例指定数据上下文对象和注册仓库服务。
public class EfCoreConfigureService : IConfigureService
    {
        public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
        {
            services.AddDbContext<OrderDbContext>(opt =>
                    opt.UseMySql(configuration.GetConnectionString("Default"),
                        ServerVersion.AutoDetect(configuration.GetConnectionString("Default"))))
                .AddGenericRepository<OrderDbContext>(ServiceLifetime.Transient)
                ;
        }

        public int Order { get; } = 1;
    }

3.主机项目需要显式的引用该项目,只有这样,该项目的ConfigureServices才会被调用。

4.数据迁移,请参考

应用启动与调试

获取源码

1.使用git 克隆lms项目源代码,lms.samples存放在samples目录下

# github
git clone https://github.com/liuhll/lms.git

# gitee
git clone https://gitee.com/liuhll2/lms.git

必要的前提

  1. 服务注册中心zookeeper
  2. 缓存服务redis
  3. mysql数据库

如果您电脑已经安装了docker以及docker-compose命令,那么您只需要进入samples\docker-compose\infrastr目录下,打开命令行工作,执行如下命令就可以自动安装zookeeperredismysql等服务:

docker-compose -f .\docker-compose.mysql.yml -f .\docker-compose.redis.yml -f .\docker-compose.zookeeper.yml up -d

数据库迁移

需要分别进入到各个微服务模块下的EntityFrameworkCore项目(例如:),执行如下命令:

dotnet ef database update

例如: 需要迁移account模块的数据库如下所示:

order模块和stock模块与account模块一致,在服务运行前都需要通过数据库迁移命令生成相关数据库。

  1. 数据库迁移指定数据库连接地址默认指定的是appsettings.Development.yml中配置的,您可以通过修改该配置文件中的connectionStrings.default配置项来指定自己的数据库服务地址。
  2. 如果没有dotnet ef命令,则需要通过dotnet tool install --global dotnet-ef安装ef工具,请[参考] (https://docs.microsoft.com/zh-cn/ef/core/get-started/overview/install)

以项目的方式启动和调试

使用visual studio作为开发工具

进入到samples目录下,使用visual studio打开lms.samples.sln解决方案,将项目设置为多启动项目,并将网关和各个模块的微服务主机设置为启动项目,如下图:

设置完成后直接启动即可。

使用rider作为开发工具进入到samples目录下,使用rider打开lms.samples.sln解决方案,打开各个微服务模块下的Properties/launchSettings.json,点击图中绿色的箭头即可启动项目。

启动网关项目后,可以看到应用接口的服务条目生成的swagger api文档 http://localhost:5000/swagger

默认的环境变量为: Development,如果需要修改环境变量的话,可以通过Properties/launchSettings.json下的environmentVariables节点修改相关环境变量,请参考在 ASP.NET Core 中使用多个环境

数据库连接、服务注册中心地址、以及redis缓存地址和分布式锁连接等配置项可以通过修改appsettings.Development.yml配置项自定义指定。

以docker-compose的方式启动和调试

进入到samples目录下,使用visual studio打开lms.samples.dockercompose.sln解决方案,将docker-compose设置为启动项目,即可启动和调式。

应用启动成功后,打开: http://127.0.0.1/swagger,即可看到swagger api文档

以docker-compose的方式启动和调试,则指定的环境变量为:ContainerDev

数据库连接、服务注册中心地址、以及redis缓存地址和分布式锁连接等配置项可以通过修改appsettings.ContainerDev.yml配置项自定义指定,配置的服务连接地址不允许为: 127.0.0.1或是localhost

测试和调式

服务启动成功后,您可以通过写入/api/account-post接口和/api/product-post新增账号和产品,然后通过/api/order-post接口进行测试和调式。

开源地址

github: https://github.com/liuhll/lms

gitee: https://gitee.com/liuhll2/lms

到此这篇关于通过lms.samples熟悉lms微服务框架的使用的文章就介绍到这了,更多相关lms微服务框架内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Idea springboot如何实现批量启动微服务

    概要 在使用IDEA开发微服务的时候,微服务比较多,启动起来比较麻烦,下面介绍一下使用批量启动微服务的方法. 方法 编辑当前项目根目录下的 .idea\workspace.xml 文件. 找到 <component name="RunDashboard"> 在这个标签下增加: <option name="configurationTypes"> <set> <option value="SpringBootAppl

  • Intellij IDEA中启动多个微服务(开启Run Dashboard管理)

    刚接触了一个微服务架构的项目,了解到了启动方式,记录一下 1.找到workspace.xml 2.打开workspace.xml,找到其中的配置项 RunDashboard 加入如下内容: <option name="configurationTypes"> <set> <option value="SpringBootApplicationConfigurationType" /> </set> </optio

  • 详解IDEA启动多个微服务的配置方法

    使用IDEA开发微服务项目,需要启动多个微服务,可以开启IDEA的Run DashBoard窗口,需要对IDEA中指定工程的父工程进行配置进行修改. 首先找到.idea文件下的workspace.xml,并找到RunDashboard 加入如下配置: <option name="configurationTypes"> <set> <option value="SpringBootApplicationConfigurationType"

  • 详解Java 微服务架构

    一.传统的整体式架构 传统的整体式架构都是模块化的设计逻辑,如展示(Views).应用程序逻辑(Controller).业务逻辑(Service)和数据访问对象(Dao),程序在编写完成后被打包部署为一个具体的应用.如图所示: 系统的水平扩展 如果要对系统进行水平扩展,通常情况下,只需要增加服务器的数量,并将打包好的应用拷贝到不同的服务器,然后通过负载均衡器(Nginx)就可以轻松实现应用的水平扩展. 整体式架构的缺点 应用复杂度增加,更新.维护困难. 易造成系统资源浪费. 影响开发效率. 应用

  • 新手学习微服务SpringCloud项目架构搭建方法

    这篇文章主要介绍了新手学习微服务SpringCloud项目架构搭建方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Spring的微服务框架SpringCloud受到众多公司欢迎,给大家带来一篇框架搭建入门.本次采用的版本是Spring Cloud版本为Finchley.RELEASE. 一.SpringCloud项目简介 spring cloud: 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理.服务发现.断路器.路由.微代理.

  • 详解用Spring Boot Admin来监控我们的微服务

    1.概述 Spring Boot Admin是一个Web应用程序,用于管理和监视Spring Boot应用程序.每个应用程序都被视为客户端,并注册到管理服务器.底层能力是由Spring Boot Actuator端点提供的. 在本文中,我们将介绍配置Spring Boot Admin服务器的步骤以及应用程序如何集成客户端. 2.管理服务器配置 由于Spring Boot Admin Server可以作为servlet或webflux应用程序运行,根据需要,选择一种并添加相应的Spring Boo

  • 通过lms.samples熟悉lms微服务框架的使用详解

    经过一段时间的开发与测试,终于发布了Lms框架的第一个正式版本(1.0.0版本),并给出了lms框架的样例项目lms.samples.本文通过对lms.samples的介绍,简述如何通过lms框架快速的构建一个微服务的业务框架,并进行应用开发. lms.samples项目基本介绍 lms.sample项目由三个独立的微服务应用模块组成:account.stock.order和一个网关项目gateway构成. 业务应用模块 每个独立的微服务应用采用模块化设计,主要由如下几部分组成: 主机(Host

  • spring boot微服务自定义starter原理详解

    这篇文章主要介绍了spring boot微服务自定义starter原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 使用spring boot开发微服务后,工程的数量大大增加(一定要按照领域来切,不要一个中间件客户端包一个),让各个jar从开发和运行时自包含成了一个重要的内容之一.spring boot starter就可以用来解决该问题(没事启动时别依赖于applicationContext.getBean获取bean进行处理,依赖关系

  • springcloud微服务之Eureka配置详解

    Eureka注册中心/服务发现框架 Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的.SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能. Eureka包含两个组件:Eureka Server和Eureka Client. Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Serve

  • Java微服务开发之Swagger详解

    目录 一.Swagger的作用和概念 1.Swagger 的优势 2.SwaggerUI 特点 2.SpringBoot集成Swagger 3.配置Swagger 4.实体配置 5.其他皮肤 一.Swagger的作用和概念 ​ 官方地址:https://swagger.io/ ​ Swagger 是一个规范且完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务以及 集成Swagger自动生成API文档. ​ Swagger 的目标是对 REST API 定义一个标准且和

  • 微服务架构拆分策略详解

    目录 1 微服务迁移准备 2 微服务颗粒的拆分策略 2.1 基于业务逻辑拆分 2.1.1 领域模型拆分 2.1.2 用户群体拆分 2.2 基于可扩展拆分 2.3 基于可靠性拆分 2.3.1 核心模块拆分 2.3.2 主次链路拆分 2.4 基于性能需求拆分 3 总结拆分原则 微服务架构及其演进史 微服务全景架构全面瓦解 前面我们学习了微服务的全景架构,了解到相对于传统单体架构,微服务的优势,以及系统服务化的发展趋势. 对于新启动的项目,我们在权衡之后可以大方的使用微服务架构.但其实大部分情况下,我

  • SpringCloud微服务熔断器Hystrix使用详解

    目录 什么是Hystrix Hystrix实战 总结 什么是Hystrix 在日常生活用电中,如果我们的电路中正确地安置了保险丝,那么在电压异常升高时,保险丝就会熔断以便切断电流,从而起到保护电路安全运行的作用. 在货船中,为了防止漏水和火灾的扩散,一般会将货仓进行分割,避免了一个货仓出事导致整艘船沉没的悲剧,这就是舱壁保护机制. Hystrix提供的熔断器也类似,在调用某个服务提供者时,当一定时间内请求总数超过配置的阈值,且窗口期内错误率过高,那Hystrix就会对调用请求熔断,后续的请求直接

  • spring cloud eureka微服务之间的调用详解

    微服务之间的调用如何实现 首先 你需要两个或以上的微服务模块 至于怎么创建可以参考我上一篇博客 spring cloud eureka注册中心 如果想在页面显示 那么需要先加上 compile 'org.springframework.boot:spring-boot-starter-thymeleaf' 这个thymeleaf依赖 springboot推荐使用thymeleaf模板 它的最大好处就是原型即是模板 后缀是html html文件 需要放在resources/templates文件夹

  • 在Kubernetes集群中搭建Istio微服务网格的过程详解

    目录 1.使用sealos部署快速部署K8S集群 1.1.基本环境配置 1.2.部署K8S集群 2.在K8S集群中部署Istio网格服务 2.1.下载Istio安装包 2.2.查看Istio可用的配置列表 2.3.展示Istio配置档的配置信息 2.4.查看Istio在k8s集群部署使用的YAML文件内容 1.使用sealos部署快速部署K8S集群 1.1.基本环境配置 1.设置主机名 hostnamectl set-hostname k8s-master hostnamectl set-hos

  • golang微服务框架基础Gin基本路由使用详解

    目录 概述 1. 基本路由 2. 路由参数 获取URL路径全部参数 获取URL路径单个参数 获取URL中指定的参数 获取指定默认值的参数的 概述 路由是自定义url地址执行指定的函数,良好的路由定义可以对seo起到很好的效果. 1. 基本路由 gin框架封装了http库,提供了 GET.POST.PUT.DELETE.PATCH.HEAD.OPTIONS 这些http请求方式. 使用 router.method() 来绑定路由 func (group *RouterGroup) METHOD(r

  • 深入理解DevOps+微服务框架

    单体架构 单体架构是什么 在搞懂DevOps和微服务之前,需要先搞懂什么是单体应用/单体架构.简单来说,就跟在校的一些小项目一样,项目的Demo写好了,找一台服务器安装环境,然后把jar包远程上服务器,然后跑起来服务就可以了.这个时候进行简单的服务监控也不难,如果项目出了问题,查看一下运行日志,就可以知道哪一步出问题了.如果懂一些脚本,也可以写一些脚本分析日志,解放双手监控服务器.这种单体架构就是采用瀑布流方式开发的,服务的流程就是:设计 -> 开发 -> 测试 -> 部署 . 单体B/

随机推荐