iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 5
0
Software Development

.Net微服務輕旅行30天系列 第 5

Day 5 手作りのService Registry

建立一個.Net Core Web Api的專案,取名API Gateway

在Models目錄底下簡單建一個用來標示服務的class 只包含服務名字跟位置

namespace APIGateway.Models
{
    public class MicroService
    {
        public string Name { get; set; }
        public string Location { get; set; }
    }
}

加入一個使用web api用以接受Service註冊跟註銷的Controller

using APIGateway.Models; //記得引入Models

namespace APIGateway.Controllers
{
    [Produces("application/json")]
    [Route("api/ServiceRegistry")]
    public class ServiceRegistryController : Controller
    {
        static List<MicroService> serviceList = new List<MicroService>() {
            new MicroService() { Name="API Gateway", Location="http://localhost:2324" }
        };
        // GET: api/ServiceRegistry
        [HttpGet]
        public IEnumerable<MicroService> Get()
        {
            return serviceList;
        }

        // POST: api/ServiceRegistry
        [HttpPost]
        public void Post([FromBody]MicroService service)
        {
            serviceList.Add(service);
        }

        // DELETE: api/ServiceRegistry/{serviceName}
        [HttpDelete("{serviceName}")]
        public void Delete(string serviceName)
        {
            serviceList.Remove(serviceList.First(s => s.Name == serviceName));
        }
    }
}

再建立一個.Net Core Web Api的專案, 取名ProductService
接著為了要在Service的啟動跟停止發request給ServiceRegistryController註冊跟註銷服務
需要在StartUp.cs寫一些code,

  • 在Configure方法加入參數IApplicationLifetime appLifetime,接著利用appLifeTime掛載host啟動跟停止時要實施的方法:
    appLifetime.ApplicationStarted.Register(OnStarted);
    appLifetime.ApplicationStopped.Register(OnStopped);
    在OnStarted跟OnStopped分別發送request給ServiceRegistry在開始時註冊服務,取消時註銷服務

  • 另外因為找不到如何手動關閉 ProductService我又加了一段code讓他自己關閉(利用applifetime.StopApplication())
    app.Use(next =>
    {
    return async (context) =>
    {
    await context.Response.WriteAsync("Application will shut down");
    await Task.Delay(10000);
    appLifetime.StopApplication();
    };
    });

   public class Startup
    {
        IServerAddressesFeature serverAddressesFeature;
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. 
        Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
        }

        // This method gets called by the runtime. 
        Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, 
                              IHostingEnvironment env, 
                              IApplicationLifetime appLifetime)
        {
            //serverAddressesFeature = app.ServerFeatures.Get<IServerAddressesFeature>(); 
            //Get Kestrel Uri 

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMvc();

            appLifetime.ApplicationStarted.Register(OnStarted);
            appLifetime.ApplicationStopped.Register(OnStopped);

            app.Use(next =>
            {
                return async (context) =>
                {
                    await context.Response.WriteAsync("Application will shut down");
                    await Task.Delay(10000);
                    appLifetime.StopApplication();
                };
            });
        }

        private void OnStarted() {
            Console.WriteLine("Starting");
            using (HttpClient client = new HttpClient()) {
                //string data = serverAddressesFeature.Addresses.FirstOrDefault().ToString(); //Kestrel
                string IISExpressIP = "http://localhost:4147";
                string json = JsonConvert.SerializeObject(new{ Name="ProductService",
                Location=IISExpressIP });
                HttpContent contentPost = new StringContent(json, Encoding.UTF8,
                "application/json");
                HttpResponseMessage response =
                client.PostAsync("http://localhost:2343//api/ServiceRegistry",
                contentPost).Result;                
            }
        }

        private void OnStopped() {
            using (HttpClient client = new HttpClient()) {
                string Uri = "http://localhost:2343/api/ServiceRegistry";
                string IISExpressIP = "http://localhost:4147";
                var service = new { Name = "ProductService", Location = IISExpressIP };
                string json = JsonConvert.SerializeObject(service);
                HttpContent contentPost = new StringContent(json, 
                Encoding.UTF8, "application/json");
                string requestUri = $"{Uri}/{service.Name}";
                HttpResponseMessage response = client.DeleteAsync(requestUri).Result;
            }
            Console.WriteLine("Stopped");
        }

    }
    ```
利用ctrl-F5啟動API Gateway專案,

在ProductService中設置中斷點在private void OnStopped() 
利用F5啟動ProductService,
停在中斷點時利用Postman發送get給http://localhost:2343/api/Registry
會看到ProductService已經被註冊
[
    {
    "name": "Initial",
    "location": "http://localhost:2324"
    },
    {
    "name": "ProductService",
    "location": "http://localhost:4147"
    }
]

F5繼續,直到程式中止,利用Postman發送get給http://localhost:2343/api/Registry
會看到ProductService已經被註銷
[
    {
    "name": "Initial",
    "location": "http://localhost:2324"
    }
]

# 這邊重點是我們可以利用appLifetime來處理服務的啟動或是結束事件。

這個簡單的Service Registry只是練習.Net Core下的產物, 實際上的例子多半會用第三方的套件像是ZooKeeper、Netflix Eureka來實現Service registry

另外有一個有問題的地方是,本來嘗試要自動取得ProductService的Uri,但發現只能取得Kestrel的Uri,
而無法取得最終IIS Express的Uri,所以只好先hard code了, 之後找到解法再來修正

Merry Christmas!


上一篇
Day 4 Service discovery 和 Service registry
下一篇
Day 6 微服務之間的解耦與調用 (1)
系列文
.Net微服務輕旅行30天30

尚未有邦友留言

立即登入留言