GVKun编程网logo

eShopOnContainers 知多少 [12]:Envoy gateways(eshoponcontainer讲解)

23

在这里,我们将给大家分享关于eShopOnContainers知多少[12]:Envoygateways的知识,让您更了解eshoponcontainer讲解的本质,同时也会涉及到如何更有效地.NET

在这里,我们将给大家分享关于eShopOnContainers 知多少 [12]:Envoy gateways的知识,让您更了解eshoponcontainer讲解的本质,同时也会涉及到如何更有效地.NET微服务最佳实践eShopOnContainers、ASP.NET Core基于微软微服务eShopOnContainer事件总线EventBus的实现、Catalog Service - 解析微软微服务架构 eShopOnContainers(三)、Docker在eShopOnContainer项目中的使用方法的内容。

本文目录一览:

eShopOnContainers 知多少 [12]:Envoy gateways(eshoponcontainer讲解)

eShopOnContainers 知多少 [12]:Envoy gateways(eshoponcontainer讲解)

1. 引言

在最新的 eShopOnContainers  3.0 中 Ocelot 网关被 Envoy Proxy 替换。下面就来简要带大家了解下 Envoy,并尝试梳理下为什么要使用 Envoy 替代 Ocelot。

2. Hello Envoy

ENVOY IS AN OPEN SOURCE EDGE AND SERVICE PROXY, DESIGNED FOR CLOUD-NATIVE APPLICATIONS.Enovy (信使) 是一款开源的专为云原生应用设计的服务代理

2.1. 快速体验

首先基于本地 Dockers 快速体验一下,先启动本地 Docker-Desktop,拉取 Envoy 镜像:

docker search envoy-dev
NAME                        DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
envoyproxy/envoy            Images for tagged releasesUse envoy-dev fo…   96
docker image pull envoyproxy:envoy-dev
latestPulling from envoyproxy/envoy-dev
171857c49d0fPull complete
419640447d26Pull complete
61e52f862619Pull complete
3f2a8c910457Pull complete
b2ce823b3fd3Pull complete
ec09faba9bc7Pull complete
b0b9168845d0Pull complete
39a220277151Pull complete
9081a11f5983Pull complete
1880b475bc3aPull complete
Digestsha256:cd8dbbbd8ce4c8c6eb52e4f8eebf55f29d1e597ca8311fecf9eda08b8cca813a
StatusDownloaded newer image for envoyproxy/envoy-dev:latest
docker.io/envoyproxy/envoy-dev:latest

该 Docker 镜像将包含最新版本的 Envoy 和一个基本的 Envoy 配置,可以将 10000 端口的入站请求路由到 www.google.com。下面启动容器测试:

> docker run -d --name envoy -p 10000:10000 envoyproxy/envoy-dev:latest
27e422f34b389d99e9180e47d8109a19975ccd139f42ac2f4fa9f724906b72f6
> docker ps | findstr ''envoy''
27e422f34b38        envoyproxy/envoy-dev:latest   "/docker-entrypoint.??   2 minutes ago        Up 2 minutes       0.0.0.0:10000->10000/tcp   envoy
> curl -I http://localhost:10000
HTTP/1.1 200 OK
content-type: text/html; charset=ISO-8859-1
p3p: CP="
This is not a P3P policy! See g.co/p3phelp for more info."
date: Sat, 17 Oct 2020 04:38:38 GMT
server: envoy
x-xss-protection: 0
x-frame-options: SAMEORIGIN
expires: Sat, 17 Oct 2020 04:38:38 GMT
cache-control: private
set-cookie: 1P_JAR=2020-10-17-04; expires=Mon, 16-Nov-2020 04:38:38 GMT; path=/; domain=.google.com; Secure
set-cookie: NID=204=h0EoJXNOTbQA11L-tVowqcwloS0-BCTR71IeN4irsmpubdPIIS4sU8Gco79pt1NhONAxxFdUJ46SKvbX4Ni-jKMWbSW0k_kn3fFkVrfLm7OOBbAtUWtxGGOCRJGbSNIRyOPfDB7_wMngEWW3yoFEs9diSCtZK9DWFZdtJJZtWuI; expires=Sun, 18-Apr-2021 04:38:38 GMT; path=/; domain=.google.com; HttpOnly
alt-svc: h3-Q050="
:443"; ma=2592000,h3-29=":443"; ma=2592000,h3-27=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-T050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
x-envoy-upstream-service-time: 37
transfer-encoding: chunked

PS: 请确保本地机器能访问 Google,否则 curl -I http://localhost:10000 会出错。

接下来我们进入容器内部,查看下配置文件,默认路径为 /etc/envoy/envoy.yaml

docker exec -it envoy /bin/bash
root@27e422f34b38:/# cat /etc/envoy/envoy.yaml
admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address:
      protocol: TCP
      address: 127.0.0.1
      port_value: 9901
static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address:
        protocol: TCP
        address: 0.0.0.0
        port_value: 10000
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match:
                  prefix: "/"
                route:
                  host_rewrite_literal: www.google.com
                  cluster: service_google
          http_filters:
          - name: envoy.filters.http.router
  clusters:
  - name: service_google
    connect_timeout: 30s
    type: LOGICAL_DNS
    # Comment out the following line to test on v6 networks
    dns_lookup_family: V4_ONLY
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: service_google
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: www.google.com
                port_value: 443
    transport_socket:
      name: envoy.transport_sockets.tls
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
        sni: www.google.com

我们把上面的配置文件拷贝到本地,将上面的 www.google.com 改为 www.baidu.com,将 admin.address.socket_address.address: 127.0.0.10.0.0.0,然后把配置文件命名为 envoy-baidu.yaml,然后挂载到容器的 /etc/envoy/envoy.yaml

> docker run --rm -d --name envoy-baidu-v $Home/k8s/envoy-baidu.yaml:/etc/envoy/envoy.yaml -p 9901:9901 -p 15001:15001 envoyproxy/envoy-dev:latest
> docker ps | findstr ''envoy''
f07f6a1e9305        envoyproxy/envoy-dev:latest   "/docker-entrypoint.??   2 minutes ago       Up 2 minutes        10000/tcp, 0.0.0.0:9901->9901/tcp, 0.0.0.0:15001->15001/tcp   envoy-baidu
3cd12b5f6ddd        envoyproxy/envoy-dev:latest   "
/docker-entrypoint.??   About an hour ago   Up About an hour    0.0.0.0:10000->10000/tcp              envoy
> curl -I http://localhost:15001
HTTP/1.1 200 OK
accept-ranges: bytes
cache-control: private, no-cache, no-store, proxy-revalidate, no-transform
content-length: 277
content-type: text/html
date: Sat, 17 Oct 2020 05:41:01 GMT
etag: "575e1f65-115"
last-modified: Mon, 13 Jun 2016 02:50:13 GMT
pragma: no-cache
server: envoy
x-envoy-upstream-service-time: 24

使用浏览器访问 http://localhost:9901 即可访问 envoy 管理页面,如下图所示:

2.2. 配置简介

第一次看 Envoy 的配置文件,和第一次接触 Nginx 的配置文件一样,绝对一脸懵逼。没关系,咱们来理一理。

作为一个代理,不管是 Nginx、HAProxy,还是 Envoy,其处理流程都是一样的。其首先都是要监听指定端口获取请求流量,然后分析请求数据,进行请求转发。脑补完大致流程后,再来看 Envoy 是如何组织配置信息的。先来了几个核心配置:

  • listener : Envoy 的监听地址,用来接收请求,处理入站请求。Envoy 会暴露一个或多个 Listener 来监听客户端的请求。
  • filter : 过滤器是处理入站和出站流量的链式结构的一部分。在过滤器链上可以集成很多特定功能的过滤器,例如,通过集成 GZip 过滤器可以在数据发送到客户端之前压缩数据。
  • route_config : 路由规则配置。即将请求路由到后端的哪个集群。
  • cluster : 集群定义了流量的目标端点,同时还包括一些其他可选配置,如负载均衡策略等。

整体流程如下图所示:

图片来源:https://fuckcloudnative.io/envoy-handbook/docs/gettingstarted/quick-start/

2.3. 代理 ASP.NET Core WebApi

有了上面的基础,下面尝试使用 Envoy 代理 ASP.NET Core WebApi。首先创建两个简单 API,然后创建一个 Envoy 配置文件,最后通过 docker compose 启动三个容器进行测试。由于项目文件结构简单,这里不再过多阐述,主要包含四个部分:

  1. City Api
  2. Weather Api
  3. Envoy 代理配置
  4. docker compose 配置

整体解决方案如下图所示。源码路径:K8S.NET.Envoy。

Envoy 代理配置基于第一节的基础上进行修改,如下所示:

admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address:
      protocol: TCP
      address0.0.0.0
      port_value9903
static_resources:
  listeners:
    - name: listener_0
      address:
        socket_address:
          protocol: TCP
          address0.0.0.0
          port_value10003
      filter_chains:
        - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
                stat_prefix: ingress_http
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: local_service
                      domains: ["*"]
                      routes:
                        - match:
                            prefix"/c"
                          route:
                            prefix_rewrite"/city"
                            cluster: city_service
                        - match:
                            prefix"/w"
                          route:
                            prefix_rewrite"/weather"
                            cluster: weather_service
                http_filters:
                  - name: envoy.filters.http.router
  clusters:
    - name: city_service
      connect_timeout0.25s
      type: LOGICAL_DNS
      # Comment out the following line to test on v6 networks
      dns_lookup_family: V4_ONLY
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: city_service
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: cityapi
                      port_value80
    - name: weather_service
      connect_timeout0.25s
      type: LOGICAL_DNS
      # Comment out the following line to test on v6 networks
      dns_lookup_family: V4_ONLY
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: weather_service
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: weatherapi
                      port_value80

以上配置 Envoy 监听 10003 端口,通过指定 prefix_rewrite 重写前缀,将 /c 路由至 cityapi/city 路径,将 /w 路由至 weatherapi/weather 路径。

docker-compose 配置如下:

version''3''
services:
  envoygateway:
    build: Envoy/
    ports:
      - "9903:9903"
      - "10003:10003"
    volumes:
      - ./Envoy/envoy.yaml:/etc/envoy/envoy.yaml
  cityapi:
    build: K8S.NET.CityApi/
    ports:
      - "8080:80"
    environment:
      ASPNETCORE_URLS"http://+"
      ASPNETCORE_ENVIRONMENT"Development"

  weatherapi:
    build: K8S.NET.WeatherApi/
    ports:
      - "8082:80"
    environment:
      ASPNETCORE_URLS"http://+"
      ASPNETCORE_ENVIRONMENT"Development"

从上可以看到,主要用来启动三个服务:

  1. envoy gateway:其中将项目路径下 /Envoy/envoy.yaml 挂载到容器目录 /etc/envoy/envoy.yaml。同时暴露 2 个端口,9903,10003。
  2. city api
  3. weather api

因此最终可以通过以下路径进行访问:

  1. http://localhost:10003/c 访问 city api。
  2. http://localhost:10003/w 访问 weather api。

执行以下命令,启动应用和代理,并测试:

> docker-compose up -d
Starting k8snetenvoy_envoygateway_1 ... done
Starting k8snetenvoy_cityapi_1      ... done
Starting k8snetenvoy_weatherapi_1   ... done
> docker-compose ps
           Name                         Command               State                         Ports
-----------------------------------------------------------------------------------------------------------------------
k8snetenvoy_cityapi_1        dotnet K8S.NET.CityApi.dll       Up      443/tcp, 0.0.0.0:8080->80/tcp
k8snetenvoy_envoygateway_1   /docker-entrypoint.sh envo ...   Up      10000/tcp, 0.0.0.0:10003->10003/tcp,
                                                                      0.0.0.0:9903->9903/tcp
k8snetenvoy_weatherapi_1     dotnet K8S.NET.WeatherApi.dll    Up      443/tcp, 0.0.0.0:8082->80/tcp

> curl http://localhost:10003/c
Shanghai
> curl http://localhost:10003/w
Cool

3. eShopOnContainers 中的应用

eShopOnContainer 中主要定义了四个 API 网关(BFF 模式),服务间通信方式主要有两种,一种是 HTTP,一种是 gRPC。如果启用 Service Mesh 并且部署至 K8S,服务整体通信架构如下图所示:

有两点需要补充说明:

  1. Linkerd 是一种 Service Mesh,其核心思想是借助 Sidecar 模式无侵入式对应用进行服务治理,包括服务发现、流量管理、负载均衡、路由等。
  2. 了解过 Istio(目前比较流行的 Service Mesh)应该知道,Envoy 在 Istio 中作为 Sidecar 而存在,而在 eShopOnContainers 中 Envoy 被充当 API Gateways。

基于上面的基础,再来看 eShopOnContainers 中的配置,其实就很明白了,主要是配置文件从 Ocelot 转变到 envoy.yaml,配置如下图所示。

路由配置如下:

  1. /m/ 、/marketing-api/ 路由至:marketing api
  2. /c/、/catalog-api/ 路由至:catalog api
  3. /o/、/ordering-api/ 路由至:ordering api
  4. /b/、/basket-api/ 路由至:basket api
  5. / 路由至:web bff aggregator api

部署时,基于 helm 将 envoy.yaml 保存至 ConfigMap,在基于 envoyproxy/enovy 镜像构建容器,将配置从 ConfigMap 挂载到容器中,容器内部即可基于配置启动 Envoy 网关了。

4. Why Envoy

经过上面的了解发现,Envoy 还是充当的网关角色,那为什么要替换呢?先来了解下 Envoy 的优势:

  • 非侵入式架构 : Envoy 基于 Sidecar 模式,是一个独立进程,对应用透明。(在 eShopOnContainer 中还是独立的网关项目,并非以 Sidecar 模式注入到服务中。)

  • 基于 C++ 开发实现:拥有强大的定制化能力和优异的性能。

  • L3/L4/L7 架构 : 传统的网络代理,要么在 HTTP 层工作,要么在 TCP 层工作。而 Envoy 同时支持 3/4 层和 7 层代理。

  • 顶级 HTTP/2 支持 : 它将 HTTP/2 视为一等公民,并且可以在 HTTP/2 和 HTTP/1.1 之间相互转换(双向),建议使用 HTTP/2

  • gRPC 支持 : Envoy 完美支持 HTTP/2,也可以很方便地支持 gRPC (gRPC 使用 HTTP/2 作为底层多路复用传输协议)。

  • 服务发现和动态配置 : 与 Nginx 等代理的热加载不同,Envoy 可以通过 API 接口动态更新配置,无需重启代理。

  • 特殊协议支持 : Envoy 支持对特殊协议在 L7 进行嗅探和统计,包括:MongoDB、DynamoDB 等。

  • 可观测性 : Envoy 内置 stats 模块,可以集成诸如 prometheus/statsd 等监控方案。还可以集成分布式追踪系统,对请求进行追踪。

再来看下 Ocelot:其本质还是 ASP.NET Core 中的一个请求中间件。只能进行 7 层代理,不支持 gRPC,不支持监控。因此总体而言,Envoy 更契合云原生对网络代理的诉求。

5. 总结

本文简要梳理了 Envoy 的基本用法,以及其在 eShopOnContainers 中的运用。Envoy 作为一个比肩 Nginx 的服务代理,其特性在 Service Mesh 中有着灵活的运用。本文就讲到这里了,下次有机会在和大家分享下 Envoy 在 Service Mesh 中的应用。

参考资料:

  1. Envoy 介绍 - Envoy 中文指南
  2. Build an API Gateway with Envoy and use with .NET Core APIs


本文分享自微信公众号 - dotNET 跨平台(opendotnet)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与 “OSC 源创计划”,欢迎正在阅读的你也加入,一起分享。

.NET微服务最佳实践eShopOnContainers

.NET微服务最佳实践eShopOnContainers

本文翻译自微软Docs, 内嵌译者多年使用的参悟,如理解有误,请不吝赐教。

微软与社区专家合作,开发了功能齐全的云原生微服务示例应用eShopOnContainers。
该应用旨在展示使用.NET、Docker以及可选的Azure,Kubernetes技术来构建电商平台。

功能 & 要求

简要回顾eShopOnContainers应用的业务功能和技术目标,示例应用代表一个出售各种实体产品的(例如T恤和咖啡杯)电商平台。

电商平台要实现的一些基本功能:

列出商品目录按类型过滤商品按品牌过滤商品将商品添加到购物车编辑或删除购物车中的物品支付注册帐号登录登出订单审核

示例应用还具有以下非功能性要求:

必须具有高可用性,并且必须自动扩展以满足不断增长的流量(并在流量减少后再缩减)。提供易于使用的运行状态监视和诊断日志,以帮助解决遇到的问题。它应该支持敏捷开发,包括对持续集成和部署(CI/CD)的支持。除了支持传统的Web前端和SPA Web前端,该应用程序还必须支持不同系统的移动客户端应用程序。支持跨平台托管和跨平台开发


Web或移动客户端通过HTTPS访问ASP.NET Core MVC服务器程序或API网关程序

API网关具有多种优势,例如将后端服务与各个前端客户端解耦,并提供更好的安全性。

该应用程序还利用了BFF模式(服务于前端的后端),该模式建议为每个前端客户端创建单独的API网关。

上面的体系图演示了基于请求是来自Web客户端还是来自移动客户端的API网关


示例应用的功能被分解为许多不同的微服务:

负责身份验证和身份列出产品目录中的商品购物车管理以及订单管理。这些独立的服务都有其自己的持久化存储,没有可以与所有服务交互的单个主数据存储,
服务之间的协调和通信是通过消息总线来完成的。



每个微服务根据其各自的需求独立设计。因此它们的技术堆栈是可以不同的(目前服务均是.NET构建并为云设计)。

简单的服务提供了基本的创建、读取、更新、删除访问(CRUD),而更高级的服务则使用领域驱动设计方法和模式来管理业务复杂性。

代码结构

因为eShopOnContainers示例程序使用微服务,其GitHub存储库中包含许多独立的项目文件。
除了独立的项目方案和可执行文件之外,各种服务还被设计为在独立的容器中运行。

下图显示了完整的Visual Studio解决方案,管理组织了各种不同的项目。

该代码被组织为支持不同的微服务,并且在每个微服务中,代码分为领域逻辑、基础设施以及用户界面/服务端点。

Ref

•  https://docs.microsoft.com/en-us/dotnet/architecture/cloud-native/introduce-eshoponcontainers-reference-app•  https://github.com/dotnet-architecture/eShopOnContainers





更多干货及最佳实践分享
关注并星标我们

本文分享自微信公众号 - dotNET跨平台(opendotnet)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

ASP.NET Core基于微软微服务eShopOnContainer事件总线EventBus的实现

ASP.NET Core基于微软微服务eShopOnContainer事件总线EventBus的实现

这个EventBus的实现是基于微软微服务https://github.com/dotnet-architecture/eShopOnContainers项目的,我把它从项目中抽离出来,打包成nuget包方便大家快速集成到项目中

从Nuget.org中安装

PM> Install-Package Toosame.EventBus.RabbitMQ -Version 1.1.2

使用

共3步:

  1. 添加事件
  2. 添加事件处理器
  3. 从控制器发布事件

1.添加事件

创建YourEvent.cs文件

复制代码
1 public class YourEvent : IntegrationEvent
2 {
3     public string Name { get; set; }
4 
5     public int Age { get; set; }
6 }
复制代码

1.添加事件处理器

创建YourEventHandler.cs文件

复制代码
1 public class YourEventHandler : IIntegrationEventHandler<YourEvent>
 2 {
 3     private readonly IConfiguration _configuration;
 4 
 5     public YourEventHandler(IConfiguration configuration){
 6         //这里只是告诉你,可以使用依赖注入的服务.
 7         _configuration = configuration;
 8     }
 9 
10     public Task Handle(YourEvent @event)
11     {
12         //你可以拿到 @event.Name
13         //你可以拿到 @event.Age
14 
15         //实现你自己的事件处理逻辑...
16     
17         return Task.CompletedTask;
18     }
19 }
复制代码

1.从控制器中发布事件

刚刚创建了一个事件,并且添加了事件处理器来处理事件,这里演示了如何发布事件;虽然刚刚添加了事件处理器,但是没有将事件处理器注册到ASP.NET Core中,下面的安装环境将演示如何注册。

复制代码
1 public class HomeController : Controller
 2 {
 3     private readonly IEventBus _eventBus;
 4 
 5     public YourEventHandler(IEventBus eventBus){
 6         _eventBus = eventBus;
 7     }
 8 
 9     [HttpGet]
10     public IAcionResult Index(){
11         _eventBus.Publish(new YourEvent(){
12             Name: "my name",
13             Age: 22
14         })
15     }
16 }
复制代码

安装:注册事件和事件处理器

共2步:

  1.配置appsettings.json

  2.在Startup.cs中安装

1.配置appsettings.json

复制代码
{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "RabbitMQ": {
    "EventBusConnection": "<yourRabbitMqHost>[:port(default 5672)]",
    "EventBusUserName": "<rabbitMqUserName>",
    "EventBusPassword": "<rabbitMqPassword>",
    "EventBusRetryCount": 5,
    "EventBusBrokeName": "<rabbitMqExchangeName>",
    "SubscriptionClientName": "<queueName>" //在微服务中,不同的微服务的应该是不同的名字
  }
}
复制代码

2.在Startup.cs中安装

 

经典安装:

复制代码
1 public void ConfigureServices(IServiceCollection services)
 2 {
 3     services.AddEventBus(Configuration.GetSection("RabbitMQ").Get<RabbitMQOption>(),
 4                     eventHandlers =>
 5                     {
 6                         eventHandlers.AddEventHandler<YourEventHandler1>();
 7                         eventHandlers.AddEventHandler<YourEventHandler2>();
 8                     });
 9 
10     services.AddMvc()
11         .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
12 }
13 
14 public void Configure(IApplicationBuilder app, IHostingEnvironment env)
15 {
16     app.UseEventBus(eventBus =>
17     {
18         eventBus.Subscribe<YourEvent1, YourEventHandler1>();
19         eventBus.Subscribe<YourEvent2, YourEventHandler2>();
20     });
21 
22     app.UseMvc();
23 }
复制代码

请把YourEvent和YourEventHandler换成你自己的事件和事件处理器

使用Autofac安装:

请先安装Autofac.Extensions.DependencyInjection这个包再使用以下代码

复制代码
1 public IServiceProvider ConfigureServices(IServiceCollection services)
 2 {
 3     services.AddMvc()
 4         .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
 5         .AddControllersAsServices();
 6 
 7     return services.AddEventBusAsAutofacService(Configuration.GetSection("RabbitMQ").Get<RabbitMQOption>(),
 8                 eventHandlers =>
 9             {
10                 eventHandlers.AddEventHandler<YourEventHandler1>();
11                 eventHandlers.AddEventHandler<YourEventHandler2>();
12             });
13 }
14 
15 
16 public void Configure(IApplicationBuilder app, IHostingEnvironment env)
17 {
18     app.UseEventBus(eventBus =>
19     {
20         eventBus.Subscribe<YourEvent1, YourEventHandler1>();
21         eventBus.Subscribe<YourEvent2, YourEventHandler2>();
22     });
23 
24     app.UseMvc();
25 }
复制代码

这样刚刚我们创建的EventHandler就能正常的收到事件了;

注意:不同微服务通过事件总线交换消息,Event的名字在不同的微服务项目中必须一致,因为RabbitMQ是通过事件名找队列(一个队列对应一个微服务)

项目github:https://github.com/lhdboy/Toosame.EventBus

 

 
 

Catalog Service - 解析微软微服务架构 eShopOnContainers(三)

Catalog Service - 解析微软微服务架构 eShopOnContainers(三)

上一篇我们说了 Identity Service,因为其基于 IdentityServer4 开发的,所以知识点不是很多,今天我们来看下 Catalog Service,今后的讲解都会把不同的、重点的拿出来讲,希望大家明白。

源码分析

我们先看下它的目录结构,很标准的 webapi 目录:

首先看下 Program,跟 IdentityService 类似,多了一个 UseWebRoot (“Pics”),把 pics 这个目录设置成了 webroot,其他都一样。

在 Startup 的构造方法中,我们也看到了使用了 secret manager tool,但是多了一个参数,在这里我们看到的是 Assembly 类型,其实 secret 只需要其中的 userSecretsId 而已。

在 ConfigureServices 中,我们看到如下代码:

services.AddMvc(options =>
{
options.Filters.Add(typeof(HttpGlobalExceptionFilter)); }).AddControllersAsServices();

添加了一个 filter,这个 HTtpGlobalExceptionFilter 可以在项目中找到,大概的意思就是遇到抛出 CatalogDomainException 类型的错误时,返回特定的错误码。

AddControllersAsServices 这个扩展方法是把项目中的 Controller 都注册到 Services 中,我们看下源码:

public static IMvcCoreBuilder AddControllersAsServices(
this IMvcCoreBuilder builder) {
var feature = new ControllerFeature(); builder.PartManager.PopulateFeature(feature); foreach (var controller in feature.Controllers.Select(c => c.AsType())) { builder.Services.TryAddTransient(controller,
controller); } builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>()); return builder; }


中间那段 foreach 就是,这样我们在项目中通过依赖注入方式都能方便的访问到各个 controller 了。

Going down:

services.AddDbContext<CatalogContext>(options =>
  {
      options.UseSqlServer(Configuration["ConnectionString"],
               sqlServerOptionsAction: sqlOptions =>
             {
                  sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);                                         //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency 
                    sqlOptions.EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
             });          
// Changing default behavior when client evaluation occurs to throw. // Default in EF Core would be to log a warning when client evaluation is performed.
options.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); //Check Client vs. Server evaluation: https://docs.microsoft.com/en-us/ef/core/querying/client-eval });


对 DBContext 的配置的时候,这里使用了 Connection Resiliency(弹回连接)的方式,其中可以看到使用 migration 的时候,它使用了 MigrationsAssembly (AssemblyName),这种方式跟我之前讲的 FluentNhibernate 有点类似,EnableRetryOnFailure 设置了这个 Action 的失败尝试机制,如果 Migration 的时候遇到 Failure,就会自动重试,这种方式避免了 app 与 database 分离造成的连接偶尔失败造成的影响。为什么会有这个机制呢?因为当我们的 database 在云端的时候,比如 Azure SQL,不可避免的会出现网络连接问题,即使我们把 app 和 database 放在一个数据中心中,我相信偶尔也会有这个问题,我们现在可以通过配置,使其如果遇到失败就会重新操作,一定程度避免了网络偶尔造成的问题。你也可以设置一些策略,使其能够在运行命令的时候能够进行重试 EF 默认情况下只是记录 client evaluation 中的 warns,我们可以通过 ConfigureWarnings 使其抛出这个警告,你也可以配置成忽略。

接下来我们看到如下代码:

services.Configure<CatalogSettings>(Configuration);

我们可以在 eShop 的各个项目中都能找到类似的语句,它会把一些项目相关的 Settings 注册到 services 中,使其成为环境变量,我们可通过 setting.json 进行配置。除了通过 setting.json 进行配置,我们还能通过 Docker run –e 进行灵活化配置。

在这里我们的 CatalogSetting 含有一个 ExternalCatalogBaseUrl 属性,我们在 docker run 的时候可以输入如下命令:

docke run -e "ExternalCatalogBaseUrl=http://localhost:5011/" ....

这样就能灵活的通过 docker 命令进行配置了,非常方便,我们也可以通过 - e 对我们 setting.json 中的变量进行赋值,比如 ConnectionString, 你可以通过点击了解更多相关内容。

// Add framework services.
            services.AddSwaggerGen();
            services.ConfigureSwaggerGen(options =>
            {
                options.DescribeAllEnumsAsStrings();
                options.SingleApiVersion(new Swashbuckle.Swagger.Model.Info()
                {
                    Title = "eShopOnContainers - Catalog HTTP API",
                    Version = "v1",
                    Description = "The Catalog Microservice HTTP API. This is a Data-Driven/CRUD microservice sample",
                    TermsOfService = "Terms Of Service"
                });
            });

            services.AddCors(options =>
            {
                options.AddPolicy("CorsPolicy",
                    builder => builder.AllowAnyOrigin()
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials());
            });


上面两段代码,分别配置了 SwaggerGen 和 Cors(跨域)策略,SwaggenGen 是一个非常实用的框架,它能自动把我们的 api 转为 web 方式呈现在我们眼前,还能进行调试,非常好用。Cors 的配置这里用的不好,它允许了所有请求,建议还是按照实际需求来吧,否则没有跨域设置的意义了。

接下来我们看到了一系列的 add service 的操作,都是关于 EventBus 的,稍微看了下,发现目前只做了 log 的动作,我们看下代码:

if (raiseProductPriceChangedEvent) // Save and publish integration event if price has changed{    //Create Integration Event to be published through the Event Bus
    var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice);    // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction
    await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(priceChangedEvent);    // Publish through the Event Bus and mark the saved event as published
    await _catalogIntegrationEventService.PublishThroughEventBusAsync(priceChangedEvent);
}


上面的代码意思是在价格有变动的时候,我们就调用 EventService 进行保存,同时对操作进行了记录。PublishThroughEventBusAsync 方法则对这条记录的 State 更改为 published。目前来说我不太清楚为何要用这种方式,也不知道为何取名为 EventBus,不过我在项目的 issue 中已经提出了这个问题,希望项目的开发者们能给我一个答案。我有查看了 Basket.Api,在这个项目中会有订阅行为,具体的等到下一章我们再仔细看看。

ok,我们再看下 Configure 方法,下面一段代码我们可以学习下:

var context = (CatalogContext)app
            .ApplicationServices.GetService(typeof(CatalogContext));

WaitForSqlAvailability(context, loggerFactory);

我们看到在这里它调用了之前注册的 CatalogContext,它并没有通过 new 进行实例化,而是通过 GetService 的方式获取之前的注册,这样 context 所依赖的其他实例也一并带进来了,非常方便好用。

WaitForSqlAvailability 方法是对数据库可用进行尝试,因为后面它需要进行数据迁移。

CatalogService 包含了 2 个 Controller,一个是 PicController,一个是 CatalogController,PicController 仅仅是根据 ID 获取了图片,CatalogController 展示了用 webapi 如何做 CURD。

运行部署

如果你要运行 Catalog.Api,你必须安装 MSSQL 和 RabbitMQ,这次我把我的系统换成了 Win10 Pro,并在电脑上使用 Docker 安装了 MSSQL-Server-Linux 和 RabbitMQ。安装这 2 个非常简单,仅仅需要输入几条命令即可:

docker run --name mssql -e ''ACCEPT_EULA=Y'' -e ''SA_PASSWORD=Pass@word'' -p 5433:1433 -d microsoft/mssql-server-linux

docker run -d --hostname my-rabbit --name rabbitmq -p 8080:15672 -p 5672:5672 rabbitmq:3-management

ok,我们使用 docker 创建了 mssql 和 rabbitmq,这里注意一下,我把 mssql 的端口映射到了本机的 5433 上,还有 rabbitmq 的管理页面,我映射到了本机的 8080 端口,你可以通过 http://localhost:8080 进行访问。

上一篇我们说过我们可以通过 iisexpress/Kestrel 或者 docker 的形式运行因为牵涉到配置,所以这两种方式的运行有些不同。

一、iisExpress 或 Kestrel 方式下,因为刚刚我们把 mssql 和 rabbitmq 的端口都映射到了本机,所以我们只需要在 setting.json 中把数据库连接和 rabbitmq 的地址指向本机即可,如下:

{  "ConnectionString": "Server=tcp:127.0.0.1,5433;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word",  "ExternalCatalogBaseUrl": "http://localhost:5101",  "EventBusConnection": "localhost",  "Logging": {    "IncludeScopes": false,    "LogLevel": {      "Default": "Debug",      "System": "Information",      "Microsoft": "Information"
    }
  }
}


ok,Ctrl+F5,运行一下看看:

当看到上面这个页面,说明你的运行正常了,你还得测试下 api 是否运行正常,比如 Pic,比如 Items。

二、docker 中运行,参照上一篇的方式,先 publish 再 build image, 不过这里要注意一点,因为你之前的 ConnectionString 和 EventBusConnection 都是指向本机(127.0.0.1)的,所以这里必须改一下,改成主机的 ip 地址或者是对应容器的 ip 也可以,如果您不想更改的话,也可以通过 docker -e 进行设置,比如:

docker run -p 8899:80 --name catalog -e "EventBusConnection=172.17.0.2" -d catalog:01

我这里的 172.17.0.2 是我 rabbitmq 容器的 ip 地址,你可以通过 docker inspect containerId 进行查看容器的 ip。

如果一切配置都正确的话,你就可以通过浏览器 http://localhost:8899 进行浏览了。

当然,除了正常浏览外,你还需测试下 api 是否正常。

困惑

在这个项目中有一些疑惑,希望大家能够给我答案。

Connection Resiliency,我看了很久,字面意思是弹性连接,但我觉得用弹性好像不太适合,一般来讲我们说的弹性都是指架构或者系统的伸缩性,我一开始也是从这个角度去了解,但看了很多文章,觉得它只是让我们在启动的时候,设置一些重试策略,在后面调用中可使用此策略,策略会根据你设置的重试次数、延迟时间等去自动重试,避免因为偶尔的错误造成的影响,所以觉得用弹回比较恰当。

EventBus,我感觉很奇怪,为什么一定要取这个名字呢?在 Android 中,很明确的,它是进行订阅发布,消息传递,可以解耦发布者和订阅者,但在 Catalog.Api 里,变成了记录操作,没有看到解耦,也没有看到订阅。在我的理解中,应该在 Startup 进行订阅操作,发布者 CatalogController 在进行 update 操作的时候,订阅者进行 add log 动作,但在这个实例中,我看到的是同步进行了这些操作,所以很不解。

Mssql-server-linux,当你用 Docker 安装了以后,你却不能使用 visual studio 2017 的 sql server data tools 进行查询(只能进行连接),为了查看效果,还需要安装 Microsoft Sql Server Management Studio(必须 17 版本以后)进行查看数据。

写在最后

这次的文章来的比较晚,一方面有点忙,另一方面就是上面提到的困惑,面对困惑我试着去解答,但有时候真的无法解答,所以提出来集思广益。

后面可能会比较慢,需要学习的东西真多,一边写一边学习成为这次系列的乐趣,现在每天坚持 6 公里快走,夜走能够是我保持头脑清晰,思考项目中的疑问,现在发觉生活越发有趣。

或许有很多人觉得只看了 Startup 就够了吗?其实真不够,我目前先把框架的源码过一遍,后面会分篇讲述,比如 Connection Resiliency。

最后应大家要求,我建了一个 QQ 群:376248054,大家可以进来一起探讨,一起学习!

相关文章:

  • 事件总线(Event Bus)知多少

  • Identity Service - 解析微软微服务架构 eShopOnContainers(二)

  • 开篇有益 - 解析微软微服务架构 eShopOnContainers(一)

原文地址:http://www.cnblogs.com/inday/p/catalog-service-eshopOnContainers.html


.NET 社区新闻,深度好文,微信中搜索 dotNET 跨平台或扫描二维码关注

本文分享自微信公众号 - dotNET 跨平台(opendotnet)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与 “OSC 源创计划”,欢迎正在阅读的你也加入,一起分享。

Docker在eShopOnContainer项目中的使用方法

Docker在eShopOnContainer项目中的使用方法

https://www.cnblogs.com/sheng-jie/p/9795996.html

 

环境准备

  1. Win10(开启Hyper-V)
  2. .NET Core SDK
  3. Docker for Windows
  4. VS2017 or VS Code
  5. Git
  6. SQL Server Management Studio(可选)
  7. Redis Desktop Manager(可选)
  8. Node.js(可选)
  9. Bower(可选)

Clone代码到本地

执行命令git clone https://github.com/dotnet/eShopOnContainers.gitclone代码到本地。使用默认DEV分支即可,该分支会保持最新改动,不要切换到其他分支。

git clone

环境设置

  1. 给Docker分配cpu和内存
    温馨提示:以下的配置是最低需求,否则不保证项目能正常运行!

    内存和CPU设置

  2. 设置共享驱动器

    驱动器共享

  3. 设置镜像加速
    把DaoCloud提供的镜像加速地址http://f1361db2.m.daocloud.io加到"registry-mirrors"的数组里,点击 Apply 。

    镜像加速

  4. 勾选试验功能
    如果遇到这个报错,Docker: “no matching manifest for windows/amd64 in the manifest list entries”,就勾选下面这个参数。

    Experimental features

  5. 防火墙设置
    打开代码路径,找到cli-windows\add-firewall-rules-for-sts-auth-thru-docker.ps1power shell脚本并执行,打开本地防火墙中的端口,以便对STS(Security Token Service container)进行身份验证。

    eShopOnContainers 防火墙入站规则设置

构建并部署到本机Docker主机

进入eShopOnContainers根目录,执行docker-compose build

构建

去喝几杯咖啡或者打个盹吧,估计得好一会。完成后,执行docker image,可以查看已经构建好的镜像。

镜像列表

PS:你可能会看到很多为<none>的镜像,这些是临时镜像,可以通过命令docker rmi $(docker images -f "dangling=true" -q)删除。

接下来执行docker-compose up命令部署到本地Docker主机。

运行镜像

启动完成后,执行docker ps命令查看已启动的容器列表。

运行中的容器列表

本地访问http://localhost:5100/,即可打开MVC Web应用。如下图所示:

MVC Web

测试所有应用程序和微服务

一旦完成容器部署,就可以从本地开发机器通过一下URL或连接字符串任何服务。

 

其他

PS:关于如何从外部连接Sql Server数据库,使用:
Server:localhost,5433 (这里是逗号,不是冒号!!!)
User:sa
Pwd:Pass@word

连接到容器内SQL Server数据库

关注我的公众号『微服务知多少』,我们微信不见不散。 
阅罢此文,如果您觉得本文不错并有所收获,请【打赏】或【推荐】,也可【评论】留下您的问题或建议与我交流。 你的支持是我不断创作和分享的不竭动力!
作者:『圣杰』 出处:http://www.cnblogs.com/sheng-jie/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。

今天关于eShopOnContainers 知多少 [12]:Envoy gatewayseshoponcontainer讲解的分享就到这里,希望大家有所收获,若想了解更多关于.NET微服务最佳实践eShopOnContainers、ASP.NET Core基于微软微服务eShopOnContainer事件总线EventBus的实现、Catalog Service - 解析微软微服务架构 eShopOnContainers(三)、Docker在eShopOnContainer项目中的使用方法等相关知识,可以在本站进行查询。

本文标签:

上一篇dotNet Core 3.1 使用 Aspose (部署 Docker)(.net core部署到docker)

下一篇使用 docker 构建分布式调用链跟踪框架skywalking(使用docker搭建分布式集群)