ServiceHelper.cs:
public class ServiceHelper : IServiceHelper
{
public async Task< string > GetOrder ( )
{
string serviceUrl = "http://localhost:9060" ;
var Client = new RestClient ( serviceUrl) ;
var request = new RestRequest ( "/orders" , Method. GET) ;
var response = await Client. ExecuteAsync ( request) ;
return response. Content;
}
public async Task< string > GetProduct ( )
{
string serviceUrl = "http://localhost:9050" ;
var Client = new RestClient ( serviceUrl) ;
var request = new RestRequest ( "/products" , Method. GET) ;
var response = await Client. ExecuteAsync ( request) ;
return response. Content;
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
Startup.cs:
public class Startup
{
public Startup ( IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get ; }
public void ConfigureServices ( IServiceCollection services)
{
services. AddControllersWithViews ( ) ;
services. AddSingleton < IServiceHelper, ServiceHelper> ( ) ;
}
public void Configure ( IApplicationBuilder app, IWebHostEnvironment env)
{
if ( env. IsDevelopment ( ) )
{
app. UseDeveloperExceptionPage ( ) ;
}
else
{
app. UseExceptionHandler ( "/Home/Error" ) ;
}
app. UseStaticFiles ( ) ;
app. UseRouting ( ) ;
app. UseAuthorization ( ) ;
app. UseEndpoints ( endpoints =>
{
endpoints. MapControllerRoute (
name : "default" ,
pattern : "{controller=Home}/{action=Index}/{id?}" ) ;
} ) ;
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
HomeController.cs:
public class HomeController : Controller
{
private readonly ILogger< HomeController> _logger;
private readonly IServiceHelper _serviceHelper;
public HomeController ( ILogger< HomeController> logger, IServiceHelper serviceHelper)
{
_logger = logger;
_serviceHelper = serviceHelper;
}
public async Task< IActionResult> Index ( )
{
ViewBag. OrderData = await _serviceHelper. GetOrder ( ) ;
ViewBag. ProductData = await _serviceHelper. GetProduct ( ) ;
return View ( ) ;
}
public IActionResult Privacy ( )
{
return View ( ) ;
}
[ ResponseCache ( Duration = 0 , Location = ResponseCacheLocation. None, NoStore = true ) ]
public IActionResult Error ( )
{
return View ( new ErrorViewModel { RequestId = Activity. Current?. Id ?? HttpContext. TraceIdentifier } ) ;
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
Index.cshtml:
@{
ViewData[ "Title" ] = "Home Page" ;
}
< div class = "text-center" >
< h1 class = "display-4" > Welcome< / h1>
< p>
@ViewBag. OrderData
< / p>
< p>
@ViewBag. ProductData
< / p>
< / div>
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
代码比较简单,这里就不用 docker
了,直接控制台启动,使用浏览器访问:
一切正常。进行到这里,各个服务也独立运行了,客户端也能正常调用了,貌似算是完成一个简易的微服务了。但是,微服务架构最重要的原则就是——“高可用”。以上的做法明显不能满足高可用性,因为任何一个服务挂掉,所有依赖这个服务的业务系统都会受影响。
停止一下订单服务:
docker stop orderservice
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
订单服务停止,导致客户端业务系统无法获取订单数据。要解决这个问题,很容易想到:集群。
四、简单的服务集群
既然单个服务实例有挂掉的风险,那么部署多个服务实例就好了嘛,只要大家不同时全挂就行。
使用 docker
运行多个服务实例:
docker run -d -p 9061 :80 --name orderservice1 orderapi
docker run -d -p 9062 :80 --name orderservice2 orderapi
docker run -d -p 9051 :80 --name productservice1 productapi
docker run -d -p 9052 :80 --name productservice2 productapi
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
现在订单服务和产品服务都增加到3个服务实例。
那么稍微改造一下客户端代码吧:ServiceHelper.cs
:
public class ServiceHelper : IServiceHelper
{
public async Task< string > GetOrder ( )
{
string [ ] serviceUrls = { "http://localhost:9060" , "http://localhost:9061" , "http://localhost:9062" } ;
var Client = new RestClient ( serviceUrls[ new Random ( ) . Next ( 0 , 3 ) ] ) ;
var request = new RestRequest ( "/orders" , Method. GET) ;
var response = await Client. ExecuteAsync ( request) ;
return response. Content;
}
public async Task< string > GetProduct ( )
{
string [ ] serviceUrls = { "http://localhost:9050" , "http://localhost:9051" , "http://localhost:9052" } ;
var Client = new RestClient ( serviceUrls[ new Random ( ) . Next ( 0 , 3 ) ] ) ;
var request = new RestRequest ( "/products" , Method. GET) ;
var response = await Client. ExecuteAsync ( request) ;
return response. Content;
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
当然拿到这些服务地址可以自己做复杂的负载均衡策略,比如轮询,随机,权重等等 都行,甚至在中间弄个 nginx
也可以。这些不是重点,所以就简单做一个随机吧,每次请求来了随便访问一个服务实例。
浏览器测试一下: 可以看到请求被随机分配了。但是这种做法依然不安全,如果随机访问到的实例刚好挂掉,那么业务系统依然会出问题。简单处理思路是:1.如果某个地址请求失败了,那么换一个地址接着执行。2.如果某个地址的请求连续多次失败了,那么就移除这个地址,下次就不会访问到它了。。。。。。。业务系统实现以上逻辑,基本上风险就很低了,也算是大大增加了系统可用性了。
🤔然后思考另一个问题:
实际应用中,上层的业务系统可能非常多,为了保证可用性,每个业务系统都去考虑服务实例挂没挂掉吗?而且实际应用中服务实例的数量或者地址大多是不固定的,例如双十一来了,流量大了,增加了一堆服务实例,这时候每个业务系统再去配置文件里配置一下这些地址吗?双十一过了又去把配置删掉吗?显然是不现实的,服务必须要做到可灵活伸缩。
这时候就引入一个名词:服务注册与发现 ,下一篇介绍。
评论记录:
回复评论: