基于.net core微服务(Consul、Ocelot、Docker、App.Metrics+InfluxDB+Grafana、Exceptionless、数据一致性、Jenkins)

时间:2019-05-02 21:33:28   收藏:0   阅读:866

1、微服务简介

一种架构模式,提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(RESTful API)。每个服务都围绕着具体的业务进行构建,并且能够被独立地部署到生产环境、类生产环境等。应尽量避免统一的、集中式的服管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建。                     ——马丁•福勒

1.1、.net core下的微服务构件

1.2、微架构

技术分享图片

2、Consul

http api官方文档地址:https://www.consul.io/api/index.html

Api本地url: http://localhost:8500/v1/agent/services

2.1、Consul是什么

是一个服务管理软件。支持多数据中心下,分布式高可用的,服务发现和配置共享。consul支持健康检查允许存储键值对。一致性协议采用 Raft 算法,用来保证服务的高可用。成员管理和消息广播 采用GOSSIP协议,支持ACL访问控制。

服务注册:一个服务将其位置信息在“中心注册节点”注册的过程。该服务一般会将它的主机IP地址以及端口号进行注册,有时也会有服务访问的认证信息,使用协议,版本号,以及关于环境的一些细节信息。

服务发现:服务发现可以让一个应用或者组件发现其运行环境以及其它应用或组件的信息。用户配置一个服务发现工具就可以将实际容器跟运行配置分离开。常见配置信息包括:ip、端口号、名称等。

2.2、述语

2.3、部署结构图

技术分享图片

2.4、命令

2.5、常用API

consul的主要接口是RESTful HTTP API,该API可以用来增删查改nodes、services、checks、configguration。所有的endpoints主要分为以下类别:

 

2.6、使用consul

2.7、项目实例

2.8、Consul DNS

DnsAgent.exe作为DNS工具

[
  {
  "Pattern": "^.*\\.consul$",
  "NameServer": "127.0.0.1:8600",
  "QueryTimeout": 1000,
  "CompressionMutation": false
  }
]

访问地址:http://服务名称.service.consul

3、Ocelot

github地址:https://github.com/TomPallister/Ocelot

Ocelot的目标是使用.NET运行微服务/面向服务架构,我们需要一个统一的入口进入我们的服务,提供监控、鉴权、负载均衡等机制,也可以通过编写中间件的形式,来扩展Ocelot的功能。  Ocelot是一堆特定顺序的中间件。

技术分享图片

3.1、Ocelot使用

4、Docker

容器是一个打包了应用服务的环境。它是一个轻量级的虚拟机,每一个容器由一组特定的应用和必要的依赖库组成。

技术分享图片

4.1、Docker-镜像常用命令 

docker images:查看本地镜像,docker images ubu*,通配符查看
docker inspect ubuntu:查看镜像详细信息
docker search aspnetcore:搜索docker hub上符合要求的镜像
docker pull microsoft/aspnetcore:拉取镜像,在run时不用从docker hub拉取
docker rmi 镜像ID1 镜像ID2:删除镜像ID1和ID2,如果强制删除加-f

4.2、Docker-容器常用命令

docker create ubuntu:14.04:创建容器,处于停止状态
docker ps:查看运行的容器,加-a查看所有容器。加-l查询出最后创建的容器,加-n=3查看最后创建的3个容器
docker start 容器名:运行已存在的容器
docker stop 容器名:停止容器
docker rm 容器名:删除容器,docker rm $(docker ps -a -q)删除所有容器
docker run -i -t --name ubuntu14 ubuntu:14.04 /bin/bash:运行一个ubuntu14.04的,带终端的容器,名字叫ubuntu14 ,-i用于打开容器的标准输入,-t让容器建立一个命令行终端
docker run --name back_ubuntu14 -d ubuntu:14.04 /bin/sh -c "while true;do echo hello world;sleep 1;done":-d是后台开容器
docker attach 容器名:依附容器
docker logs -f --tail=5  back_ubuntu14:查看最近的5条日志
docker top 容器名:查看容器进程 
docker inspect 容器名:查看容器信息,查看具体子项docker inspect --format={{.NetworkSettings.IPAddress}}  back_ubuntu14
docker export 容器名 >容器存储名称.tar:导出容器
win powershell下  docker export 容器ID -o 名字.tar
docker import 容器存储名称.tar:导入镜像
docker commit -m="abc" --author="gsw" 容器ID  镜像名称:提交容器到本地镜像

4.4、Docker-Dockerfile

FROM:指定待扩展的父级镜像。除了注释外,在文件开头必须是一个FROM指令,接下来的指令便在这个父级镜像的环境中运行,直到遇到下一个FROM指令。通过添加多个FROM指令,可以在同一个Dockerfile文件中创建多个镜像。
MAINTAINER:用来声明创建的镜像的作都信息。非必需
RUN:用来修改镜像命令,常用来安装库、程序 以及配置程序。一条RUN指令执行完毕后,会在当前镜像上创建一个新的镜像层,接下来的指令会在新的镜像上继续执行。
EXPOSE:用来指明容器内进程对外开放的端口,多个端口之间使用空格隔开。运行容器时,通过参数-P(大写)即可将EXPOSE里所指定的端口映射到主机上国外的坠机端口,其队容器或主机就可以通过映射后的端口与此容器通信。同时,我们也可以通过-p(小写)参数将Dockerfile中EXPOSE中没有的端口设置成公开的。
ADD:向新镜像中添加文件,这个文件可以是一个主机文件,也可以是一个网络文件,也可以是一个文件夹。
VOLUME:在镜像里创建一个指定路径的挂载点,这个路径可以来自主机或都其他容器。多个容器可以通过同一个挂载点共享数据,即便其中一个容器已经停止,挂载点也仍然可以访问,只有当挂载点的容器引用全部消失时,挂载点才会自动删除。
WORKDIR:为接下来执行的指令指定一个新的工作目录,这个目录可以是绝对目录,也可以是相对目录。
ENV:设置容器运行的环境变量。在运行容器的时候,通过-e参数可以修改这个环境变量值 ,也可以添加新的环境变量
CMD:用来设置启动容器时默认运行的命令。
ENTRYPOINT:与CMD类似,它也是用来指定容器启动时默认运行的命令。
USER:为容器的运行及接下来RUN、CMD、ENTRYPOINT等指令的运行指定用户或UID
ONBUILD:触发指令。构建镜像的时候,Docker的镜像构建器会将所有的ONBUILD指令指定的命令保存到镜像的元数据中,这些命令在当前镜像的构建的构建过程中并不会执行。只有新的镜像用用FRMO指令指定父镜像为这个镜像时,便会触发。

4.5、Docker生成asp.net core镜像和运行

发布asp.net core项目,并在发布文件夹下创建Dockerfile文件,复制下面内容

#父镜像
FROM microsoft/aspnetcore

#设置工作目录
WORKDIR /app

#复制发布文件到/app下
COPY . /app

#设置端口
EXPOSE 80

#使用dotnet XXXXXXXXX.dll来运行asp.net core项目,注意大小写
ENTRYPOINT ["dotnet", “XXXXXXXXX.dll"]

4.6、Docker生成asp.net core镜像和运行

docker build -t xxxxxxxxxxx:latest .
docker run -it -p 6801:6801  xxxxxxxxxxx:latest

注意:docker内部web的端口, 上述命令中,第二个端口为docker内web的端口。

5、App.Metrics+InfluxDB+Grafana

建议:建议在网关上进行监控,因为网关上监控可以监控所有

App.Metrics:https://www.app-metrics.io

InfluxDB1.5.1-1:https://portal.influxdata.com

Grafana-5.0.4:https://grafana.com/get

5.1、安装使用

5.2、配置Grafana

5.2.1、配置数据源

技术分享图片

5.2.2、配置Dashboard

我们采用模板导入模式,将项目引用 App.Metrics 并访问 App.Metrics 源地址:https://www.app-metrics.io/

技术分享图片

获取到InfluxDB对应的仪表盘编号2125,然后输入使用该模板

技术分享图片

技术分享图片

导入成功后

技术分享图片

5.3、App.Metrics监控数据采集

技术分享图片

5.4、APM-Grafana告警

6、Exceptionless

6.1、创建组织

技术分享图片

6.2、创建项目

技术分享图片

6.3、集成Exceptionless 客户端

Install-Package Exceptionless.AspNetCore

通过 API 密钥执行  app.UseExceptionless("Qa3OzvEJC9FXo9SdwwFBv6bAkVbjWQKbV6hhtYEM")  方法

6.4、示例代码

#region Exceptionless测试
try
{
    ExceptionlessClient.Default.SubmitLog("调试Exceptionless.Logging.LogLevel.Debu", Exceptionless.Logging.LogLevel.Debug);
    ExceptionlessClient.Default.SubmitLog("错误Exceptionless.Logging.LogLevel.Error", Exceptionless.Logging.LogLevel.Error);
    ExceptionlessClient.Default.SubmitLog("大错Exceptionless.Logging.LogLevel.fatal", Exceptionless.Logging.LogLevel.Fatal);
    ExceptionlessClient.Default.SubmitLog(" Exceptionless.Logging.LogLevel.Info", Exceptionless.Logging.LogLevel.Info);
    ExceptionlessClient.Default.SubmitLog(" Exceptionless.Logging.LogLevel.Off", Exceptionless.Logging.LogLevel.Off);
    ExceptionlessClient.Default.SubmitLog(" Exceptionless.Logging.LogLevel.Other", Exceptionless.Logging.LogLevel.Other);
    ExceptionlessClient.Default.SubmitLog(" Exceptionless.Logging.LogLevel.Trace", Exceptionless.Logging.LogLevel.Trace);
    ExceptionlessClient.Default.SubmitLog("Exceptionless.Logging.LogLevel.Warn", Exceptionless.Logging.LogLevel.Warn);


    var data = new Exceptionless.Models.DataDictionary();
    data.Add("data1key", "data1value");
    ExceptionlessClient.Default.SubmitEvent(new Exceptionless.Models.Event {
        Count = 1,
        Date = DateTime.Now,
        Data = data, Geo = "geo",
        Message = "message",
        ReferenceId = "referencelId",
        Source = "source",
        Tags = new Exceptionless.Models.TagSet() { "tags" },
        Type = "type",
        Value = 1.2m });
    ExceptionlessClient.Default.SubmitFeatureUsage("feature");
    ExceptionlessClient.Default.SubmitNotFound("404 not found");
    ExceptionlessClient.Default.SubmitException(new Exception("自定义异常"));

    throw new DivideByZeroException("throw DivideByZeroException的异常:" + DateTime.Now);
}
catch (Exception exc)
{
    exc.ToExceptionless().Submit();
}
#endregion

6.5、本地部署

本地部署官方wiki

下载Windows版本安装包,并进行解压,然后双击运行Start.bat即可

需要环境:

技术分享图片

6.6、项目集成

注意:本地化不能再使用 app.UseExceptionless(apiKey: "tJxBWkCbgDLCMoKKqWII3Eyw4aJOsyOCgX26Yurm"); 形式来上传日志数据,应采用另外的方式:配置文件方式

"Exceptionless": {
  "ApiKey": "tJxBWkCbgDLCMoKKqWII3Eyw4aJOsyOCgX26Yurm",
  "ServerUrl": "http://localhost:50000",
  "DefaultData": {
  },
  "DefaultTags": [ "xplat" ],
  "Settings": {
    "FeatureXYZEnabled": false
  }
}

然后修改 Startup.cs 

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    ...
    //app.UseExceptionless(apiKey: "tJxBWkCbgDLCMoKKqWII3Eyw4aJOsyOCgX26Yurm");
    //上方的方法本地化不适用
    app.UseExceptionless(Configuration);
    ...
}

搞定

技术分享图片

6.7、查询语法

技术分享图片

示例

技术分享图片

6.8、常见问题

技术分享图片

 

Invoke-WebRequest : 请求被中止: 未能创建 SSL/TLS 安全通道。

elasticsearch-XXX”,因为该路径不存在。

解决方案:编辑Start-ElasticSearch.ps1,将所需的文件全部下载下来,然后解压进行拷贝,如下图,然后在双击运行Start.bat即可

技术分享图片

帮助类:

技术分享图片
/// <summary>
/// 
/// </summary>
public static class ExceptionLessLog
{
    static bool IsInit = false;

    static ExceptionLessLog()
    {
        if (!IsInit)
        {
            #region Exceptionless配置
            ExceptionlessClient.Default.Configuration.ApiKey = "KwqNUJ5njrnOehQTSYY6yXXXXXXXXXXXXXXX";
            ExceptionlessClient.Default.Configuration.ServerUrl = "http://XXX.XXX.XXX.XXX:50000";
            ExceptionlessClient.Default.Startup();
            #endregion
        }
    }

    #region 日志功能
    /// <summary>
    /// 跟踪
    /// </summary>
    public static void Trace(string message, params string[] tags)
    {
        ExceptionlessClient.Default.CreateLog(message, LogLevel.Trace).AddTags(tags).Submit();
    }

    /// <summary>
    /// 调试
    /// </summary>
    public static void Debug(string message, params string[] tags)
    {
        ExceptionlessClient.Default.CreateLog(message, LogLevel.Debug).AddTags(tags).Submit();
    }

    /// <summary>
    /// 信息
    /// </summary>
    public static void Info(string message, params string[] tags)
    {
        ExceptionlessClient.Default.CreateLog(message, LogLevel.Info).AddTags(tags).Submit();
    }

    /// <summary>
    /// 警告
    /// </summary>
    public static void Warn(string message, params string[] tags)
    {
        ExceptionlessClient.Default.CreateLog(message, LogLevel.Warn).AddTags(tags).Submit();
    }

    /// <summary>
    /// 错误
    /// </summary>
    public static void Error(string message, params string[] tags)
    {
        ExceptionlessClient.Default.CreateLog(message, LogLevel.Error).AddTags(tags).Submit();
    }
    #endregion

    /// <summary>
    /// 异常捕获提交
    /// </summary>
    /// <param name="exception"></param>
    /// <param name="pluginContextData"></param>
    /// <param name="client"></param>
    public static void Submit(this Exception exception, ContextData pluginContextData = null, ExceptionlessClient client = null)
    {
        exception.ToExceptionless().Submit();
    }
}
View Code

7、数据一致性

技术分享图片

7.1、最终一致性

技术分享图片

  在微服务架构中,各个微服务之间通常会使用事件驱动通信和发布订阅系统实现最终一致性。

7.2、最终一致性-补偿机制

技术分享图片

7.3、幂等和防重

7.4、MassTransit

MassTransit 是一个自由、开源、轻量级的消息总线, 用于使用. NET 框架创建分布式应用程序。MassTransit 在现有消息传输上提供了一组广泛的功能, 从而使开发人员能够友好地使用基于消息的会话模式异步连接服务。基于消息的通信是实现面向服务的体系结构的可靠和可扩展的方式。

  官网地址:http://masstransit-project.com/,GitHub地址:https://github.com/MassTransit/MassTransit (目前:1590Star,719Fork)

  类似的国外开源组件还有NServiceBus,没有用过,据说MassTransit比NServiceBus更加轻量级,并且在开发之初就选用了RabbitMQ作为消息传输组件,当然MassTransit还支持Azure Service Bus。类似的国内开源组件则有园友savorboard(杨晓东)的CAP

7.5、最简单的发送/接收实例

这里以MassTransit + RabbitMQ为例子,首先请确保安装了RabbitMQ,如果没有安装,可以阅读我的RabbitMQ在Windows环境下的安装与使用去把RabbitMQ先安装到你的电脑上。另外,RabbitMQ的背景知识也有一堆,有机会也还是要了解下Exchange,Channel、Queue等内容。

技术分享图片

7.6、一对一的发布/订阅实例(类似于RabbitMQ的工作模式)

除了简单的发送/接收模式外,我们用的更多的是发布/订阅这种模式。

注意:发布方如果发布时没有订阅方,发布的数据将会丢失

7.6、一对多的发布/订阅实例(队列名不同即可)

技术分享图片

7.7、带返回状态消息的示例

之前的例子都是发布之后,不管订阅者有没有收到以及收到后有没有处理成功(即有没有返回消息,类似于HTTP请求和响应),在MassTransit中提供了这样的一种模式,并且还可以结合GreenPipes的一些扩展方法实现重试、限流以及熔断机制。这一部分详见官方文档:http://masstransit-project.com/MassTransit/usage/request-response.html

  1. 准备下图所示的三个项目:通过NuGet安装MassTransit以及MassTransit.RabbitMQ
    技术分享图片
  2.   RRDemo_Entity.Entity :准备请求和响应的消息传输类型
    public interface IRequestEntity
    {
        int ID { get; set; }
        string Name { get; set; }
    }
    public class RequestEntity : IRequestEntity
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }
    
    public interface IResponseEntity
    {
        int ID { get; set; }
        string Name { get; set; }
    
        int RequestID { get; set; }
    }
    public class ResponseEntity : IResponseEntity
    {
        public int ID { get; set; }
        public string Name { get; set; }
    
        public int RequestID { get; set; }
    }
  3.  RRDemo_Server.Program 请求接收端
    class Program
    {
        static void Main(string[] args)
        {
            Console.Title = "应答方";
            var bus = Bus.Factory.CreateUsingRabbitMq(cfg =>
            {
                var host = cfg.Host(new Uri("rabbitmq://localhost/"), hst =>
                {
                    hst.Username("guest");
                    hst.Password("guest");
                });
                cfg.ReceiveEndpoint(host, "request_response_wyt", e =>
                {
                    e.Consumer<RequestConsumer>();
                });
            });
            bus.Start();     
            Console.ReadLine();
            bus.Stop();
        }
    }
    
    public class RequestConsumer : IConsumer<IRequestEntity>
    {
        public async Task Consume(ConsumeContext<IRequestEntity> context)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            await Console.Out.WriteLineAsync($"收到请求id={context.Message.ID} name={context.Message.Name}");
            Console.ResetColor();
            var response = new ResponseEntity
            {
                ID = 22,
                Name = $"李四",
                RequestID = context.Message.ID
            };
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine($"应答ID={response.ID},Name={response.Name},RequestID={response.RequestID}");
            Console.ResetColor();
            context.Respond(response);
        }
    }
  4.  RRDemo_Client.Program 请求发送端
    static void Main(string[] args)
    {
        Console.Title = "请求方";
    
        var bus = Bus.Factory.CreateUsingRabbitMq(cfg =>
        {
            var host = cfg.Host(new Uri("rabbitmq://localhost/"), hst =>
            {
                hst.Username("guest");
                hst.Password("guest");
            });
            //重试
            cfg.UseRetry(ret =>
            {
                ret.Interval(3, 10);// 消费失败后重试3次,每次间隔10s
            });
            //限流
            cfg.UseRateLimit(1000, TimeSpan.FromSeconds(100));// 1分钟以内最多1000次调用访问
            //熔断
            cfg.UseCircuitBreaker(cb =>
            {
                cb.TrackingPeriod = TimeSpan.FromSeconds(60);//1分钟
                cb.TripThreshold = 15;// 当失败的比例至少达到15%才会启动熔断
                cb.ActiveThreshold = 10;// 当失败次数至少达到10次才会启动熔断
                cb.ResetInterval = TimeSpan.FromMinutes(5);// 当在1分钟内消费失败率达到15%或调用了10次还是失败时,暂停5分钟的服务访问
    
            });
        });
        bus.Start();
    
        var serviceAddress = new Uri($"rabbitmq://localhost/request_response_wyt");
        var client = bus.CreateRequestClient<IRequestEntity, IResponseEntity>(serviceAddress, TimeSpan.FromHours(10));
        // 创建请求客户端,10H之内木有回馈则认为是超时(Timeout)
    
        while (true)
        {
            Console.WriteLine("请出请按q,否则请按其他键!");
            string value = Console.ReadLine();
            if (value.ToLower() == "q")
            {
                break;
            }
    
            Task.Run(async () =>
            {
                var request = new RequestEntity() { ID = 1, Name = "张三" };
                var response = await client.Request(request);
    
                Console.WriteLine($"请求ID={request.ID},Name={request.Name}");
                Console.WriteLine($"应签ID={response.ID},Name={response.Name},RequestID={response.RequestID}");
            }).Wait();
        }
    
    }
  5. 效果展示
    技术分享图片

    注意:这里的请求方关闭后应答方则无法将应答再回复给请求方,会丢失

7.8、带Observer模式的发布/订阅示例

在某些场景中,我们需要针对一个消息进行类似于AoP(面向切面编程)或者监控的操作,比如在发送消息之前和结束后记日志等操作,我们可以借助MassTransit中的Observer模式来实现。(在MassTransit的消息接收中,可以通过两种模式来实现:一种是基于实现IConsumer接口,另一种就是基于实现IObserver接口)关于这一部分,详见官方文档:http://masstransit-project.com/MassTransit/usage/observers.html

7.9、数据一致性示例

详见:https://github.com/786744873/DataConsistentSample

技术分享图片

 

8、Jenkins

官方地址:https://jenkins.io/

Jenkins 是一款流行的开源持续集成(CI)与持续部署(CD)工具,广泛用于项目开发,具有自动化构建、测试和部署等功能。

  使用Jenkins的目的在于:

  (1)持续、自动地构建/测试软件项目。 
  (2)监控软件开放流程,快速问题定位及处理,提升开发效率。

8.1、Jenkins下载与安装

这里我们下载Windows版本的

技术分享图片

安装完成后会提示我们解锁 Jenkins

技术分享图片

这里选择安装推荐的插件

技术分享图片

创建管理账户 => 也可以直接使用admin账户继续

技术分享图片

配置Jenkins端口,默认8080,最好不要使用8080端口

技术分享图片

修改Jenkins服务端口,改为8080-->8081

修改安装目录下 jenkins.xml 文件

技术分享图片

然后重启Jenkins服务

8.2、持续集成Asp.Net Core项目

  1. 我们以Github上面的项目为例,github项目地址:https://github.com/786744873/first.git
    技术分享图片
  2. 配置源代码
    技术分享图片
  3. 构建触发器(这里每半小时轮询一次)
    技术分享图片
  4. 构建
    技术分享图片
    技术分享图片

    cd CITest
    cd CITest
    dotnet restore
    dotnet build
    dotnet publish -o "bin\Debug\netcoreapp2.0\publish"
    cd bin\Debug\netcoreapp2.0\publish
    docker rm clitest -f
    docker rmi clitest -f
    docker build -t clitest:latest .
    docker run -p 4555:4555 -d --name clitest clitest:latest
  5. 保存,然后去配置构建邮件发送
    Jenkins->系统管理->系统设置
    设置系统管理员收件地址(实际上这边配置的是发件人的邮箱地址):
    技术分享图片

    技术分享图片

     

  6. 继续进行项目配置
    技术分享图片

    技术分享图片

  7. 构建项目
    技术分享图片

    技术分享图片

     

 

原文:https://www.cnblogs.com/wyt007/p/10631109.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!