steeltoe簡單使用


本文源碼地址

https://github.com/wswind/Steeltoe-Sample

Steeltoe是什么

Steeltoe是幫助.NET開發的服務接入Spring Cloud技術棧的官方支持工具。也就是說,微服務的系統框架,還是由Spring Cloud來實現,而業務服務,通過.NET Core來實現。后面我們將基於Steeltoe來嘗試實現微服務系統框架。

steeltoe主要包含以下功能:斷路器,配置中心,服務連接器(MSSql、MySql、Oauth、Mongodb、Redis等),服務發現,網絡文件共享(windows),動態日志,雲管理(服務監控),雲安全(Jwt認證),開發工具(Steeltoe CLI)。steeltoe源碼結構如上圖。

steeltoe項目地址:https://github.com/SteeltoeOSS/steeltoe
steeltoe樣例地址:https://github.com/SteeltoeOSS/Samples
steeltoe文檔:https://steeltoe.io/docs/

steeltoe運行於Cloud Foundry,關於Cloud Foundry的介紹,請查看:https://blog.csdn.net/qq_30154571/article/details/84955097

Consul服務發現

參考: https://steeltoe.io/service-discovery/get-started/consul

我沒有使用docker for windows,而是在virtualbox 中創建了centos 7虛擬機運行docker,所以需要對官方教程的網絡配置有調整。如果你的環境為docker for windows,則可以直接使用localhost

我的虛擬機網絡環境為virtualbox的host only network + nat雙網卡配置。
網關ip為192.168.56.1對應開發物理機ip,虛擬機ip為192.168.56.104對應consul ip。
virtualbox的網絡環境配置,可參考我的另一篇博文https://www.cnblogs.com/wswind/p/10832740.html

在centos中通過命令運行consul:

docker pull consul
docker run -d -p 8500:8500 consul #-d意味着后台運行

通過https://start.steeltoe.io/創建模板,選擇NetCore3.1,組件選擇Discovery。

此時我們可以看到,這個模板生成器,其實創建的就是一個Asp.NET Core WebAPI的空項目,唯一的不同,就是在項目文件中添加了包依賴:Steeltoe.Discovery.ClientCore。並在ConfigureServices時,調用了

services.AddDiscoveryClient(Configuration);

通過在appsettings.json中添加配置,寫明本機地址以及consul地址,運行項目,即可完成服務注冊

  "spring": {
    "application": {
      "name": "Consul-Register-Example"
    }
  },
  "consul": {
    "host": "192.168.56.104",
    "port": 8500,
    "discovery": {
      "enabled": true,
      "register": true,
      "port": "8080",
      "ipAddress": "192.168.56.1",
      "preferIpAddress": true
    }
  }

修改Properties\launchSettings.json

"iisSettings": {
		"windowsAuthentication": false, 
		"anonymousAuthentication": true, 
		"iisExpress": {
			"applicationUrl": "http://localhost:8080",
			"sslPort": 0
		}
	}

為了便於通過192.168.56.1訪問服務,我添加了UseUrls:

 var builder = WebHost.CreateDefaultBuilder(args)
                .UseUrls("http://0.0.0.0:8080")

如果不修改,對於健康檢查貌似沒什么影響,只是你注冊上去的ip,就是無法訪問的了。

關於asp.net core如何修改綁定ip,可參考:

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-3.1#endpoint-configuration

https://www.cnblogs.com/Hai--D/p/5842842.html

運行項目后,打開consul地址,我們可以看到服務注冊已經完成了。

我們可以看到服務注冊過程非常簡單,steeltoe的服務注冊工具的使用,僅需添加幾行json配置即可完成。大大減少了我們的代碼開發量。

上面是服務注冊,接下面我們來講解服務發現。由於已經了解steeltoe的模板生成器只是在空模板上加了nuget包的引用,我們也無需再通過它生成項目了。

通過.net core cli創建空項目,並添加nuget包即可,命令行如下:

dotnet new webapi -n ConsulDiscovery
cd ConsulDiscovery
dotnet add package Steeltoe.Discovery.ClientCore

在ConfigureSerivces中,設置DI services.AddDiscoveryClient(Configuration);

然后修改appsettings.json

{
...

	"spring": {
		"application": {
			"name": "Consul-Discover-Example"
		}
	},
	"consul": {
		"host": "192.168.56.104",
		"port": 8500,
		"discovery": {
			"enabled": true,
			"register": false
		}
	}

...
}

修改WeatherForecastController,通過依賴注入IDiscoveryClient,創建DiscoveryHttpClientHandler,然后通過consul注冊的服務地址來訪問之前創建的服務。

        DiscoveryHttpClientHandler _handler;
        public WeatherForecastController(ILogger<WeatherForecastController> logger, IDiscoveryClient client)
        {
            _logger = logger;
            _handler = new DiscoveryHttpClientHandler(client);
        }

        [HttpGet]
        public async Task<string> Get()
        {
            var client = new HttpClient(_handler, false);
            return await client.GetStringAsync("http://Consul-Register-Example/api/values");
        }

啟動服務后,可以看到返回值:

上述過程的UML序列圖如下,服務注冊客戶端首先進行服務注冊,服務發現通過讀取Consul中的服務地址,來進行訪問。

Service Connectors with Microsoft SQL

首先通過生成器https://start.steeltoe.io/創建項目,選netcore 3.1 + SQL Server

創建項目默認安裝了Nuget包 Microsoft.EntityFrameworkCore.SqlServer並添加了依賴注入

services.AddSqlServerConnection(Configuration);

不過.NET Core 3.1模板目前有問題缺少了一些包引用,導致無法編譯通過,需要手動安裝

System.Data.SqlClient
Steeltoe.CloudFoundry.ConnectorCore

另外,生成的模板項目中的nuget包Microsoft.EntityFrameworkCore.SqlServer不是必須的,如果不使用EfCore其實無需引入這個包。

在appsettings.json中添加數據庫的連接配置

{
...


 "sqlserver": {
	"credentials": {
		"server": "127.0.0.1",
		"port": "1433",
		"username": "sa",
		"password": "sa"
		
	}
 }

...
}

sql server需要啟用tcp/ip以允許外部訪問,開啟后需要重啟服務

模板中,向我們展示了這個包的用法

...
    public class ValuesController : ControllerBase
    {
        private readonly SqlConnection _dbConnection;
        public ValuesController([FromServices] SqlConnection dbConnection)
        {
            _dbConnection = dbConnection;
        }

        // GET api/values
        [HttpGet]
        public ActionResult<IEnumerable<string>> Get()
        {
            List<string> tables = new List<string>();
        
            _dbConnection.Open();
            DataTable dt = _dbConnection.GetSchema("Tables");
            _dbConnection.Close();
            foreach (DataRow row in dt.Rows)
            {
                string tablename = (string)row[2];
                tables.Add(tablename);
            }
            return tables;
        }
...        

運行效果如下:

通過Dapper來配合使用 Steeltoe.CloudFoundry.ConnectorCore 應該會很方便。

不過也有一個問題,依賴注入是直接注入了SqlConnection,不清楚如果同時連接多數據庫能否拓展。不過對於微服務開發而言,這種情況很少見。

Service Connectors with Redis Cache

首先運行redis

docker run -d -p 6379:6379 redis

通過 Steeltoe Initializr創建項目選擇.NET Core3.1 + Redis

項目中添加了包:

Microsoft.Extensions.Caching.StackExchangeRedis
Steeltoe.CloudFoundry.ConnectorCore

在依賴注入時,添加了:

services.AddDistributedRedisCache(Configuration);

添加配置文件:

{
...

 "redis": {
	"client": {
		
		"host": "192.168.56.104",
		"port": "6379",
	}
 }
...
}

默認的控制器代碼如下:

    public class ValuesController : ControllerBase
    {
        private readonly IDistributedCache _cache;
        public ValuesController(IDistributedCache cache)
        {
            _cache = cache;
        }

        // GET api/values
        [HttpGet]
        public async Task<IEnumerable<string>> Get()
        {
            await _cache.SetStringAsync("MyValue1", "123");
            await _cache.SetStringAsync("MyValue2", "456");
            string myval1 = await _cache.GetStringAsync("MyValue1");
            string myval2 = await _cache.GetStringAsync("MyValue2");
            return new string[]{ myval1, myval2};
        }

運行結果如下:

Using Service Connectors with RabbitMQ

運行rabbitmq

docker run -d -p 5672:5672 rabbitmq

Steeltoe Initializr 創建.netcore 3.1 + rabbitmq

#引用包
Steeltoe.CloudFoundry.ConnectorCore
RabbitMQ.Client

#依賴注入
services.AddRabbitMQConnection(Configuration);

添加連接配置

 "rabbitmq": {
	"client": {
		"server": "192.168.56.104",
		"port": "5672",
		"username": "guest",
		"password": "guest",
		
	}
 }

注意此處官方教程中的配置有誤,此處為ip配置為server而非host,見Issue:https://github.com/SteeltoeOSS/steeltoe/issues/263

controller的樣例代碼如下,自己發送了五條消息並自己接收打印。

public ValuesController(ILogger<ValuesController> logger, [FromServices] ConnectionFactory factory)
        {
            _logger = logger;
            _factory = factory;
        }
        
        // GET api/values
        [HttpGet]
        public ActionResult<string> Get()
        {
            using (var connection = _factory.CreateConnection())
            using (var channel = connection.CreateModel())
            {
                //the queue
                channel.QueueDeclare(queue: queueName,
                             durable: false,
                             exclusive: false,
                             autoDelete: false,
                             arguments: null);
                // consumer
                var consumer = new EventingBasicConsumer(channel);
                consumer.Received += (model, ea) =>
                {
                    string msg = Encoding.UTF8.GetString(ea.Body);
                    _logger.LogInformation("Received message: " + msg);
                };
                channel.BasicConsume(queue: queueName,
                                     autoAck: true,
                                     consumer: consumer);
                // publisher
                int i = 0;
                while (i<5) { //write a message every second, for 5 seconds
                    var body = Encoding.UTF8.GetBytes($"Message {++i}");
                    channel.BasicPublish(exchange: "",
                                         routingKey: queueName,
                                         basicProperties: null,
                                         body: body);
                    Thread.Sleep(1000);
                }
            }
            return "Wrote 5 message to the info log. Have a look!";
        }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM