暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

微服务之Consul服务自动注册

码农游乐场 2021-09-08
963

安装Consul

我用的是Win 10的系统,但是安装了Docker Desktop, 然后用powershell进行docker的相关操作。

  • pull一个consul镜像到本地

PS C:\Users\Zhanwei> docker pull consul
Using default tag: latest
latest: Pulling from library/consul
31603596830f: Pull complete
e424a2a1e4f7: Pull complete
2472846eab82: Pull complete
24473b564e4a: Pull complete
b3caf5938c68: Pull complete
b6f50757bb0c: Pull complete
Digest: sha256:1cb6f7247472638c470c1bcf059a1f74a4d54c6b49c57c80651feb08cc0a80cf
Status: Downloaded newer image for consul:latest
docker.io/library/consul:latest
PS C:\Users\Zhanwei>

  • 运行一个Consul

PS C:\Users\Zhanwei> docker run -d -p 8500:8500 -v data/consul:/consul/data -e CONSUL_BIND_INTERFACE='eth0' --name=consul_server_1 consul:latest agent -server -bootstrap -ui -node=node-1 -client='0.0.0.0' -datacenter=hangzhou
9d5e1c54ee6e44efc100e5a6fdf4e4f22ad2a177e0f0d937e54bb9b245e657cd

参数说明:

  • server:以server身份启动。默认是client

  • bootstrap-expect:集群要求的最少server数量,当低于这个数量,集群即失效。

  • data-dir:data存放的目录,更多信息请参阅consul数据同步机制

  • node:节点id,集群中的每个node必须有一个唯一的名称。默认情况下,Consul使用机器的hostname

  • bind:监听的ip地址。默认绑定0.0.0.0,可以不指定。表示Consul监听的地址,而且它必须能够被集群中的其他节点访问。Consul默认会监听第一个private IP,但最好还是提供一个。生产设备上的服务器通常有好几个网卡,所以指定一个不会出错

  • client: 客户端的ip地址,0.0.0.0是指谁都可以访问(不加这个,下面的ui :8500无法访问)

  • ui: 可以访问UI界面

  • -config-dir指定配置文件夹,Consul会加载其中的所有文件

  • -datacenter 指定数据中心名称,默认是dc1

打开浏览器,如下所示,IP换成自己的IP 


这样单机版的Consul就启动完成了,接下来我们新建个API服务。

新建WEB API服务项目

  • 新建一个名为CustomMiddleware.api01的API项目。

  • 安装Consul依赖包。

PM>Install-Package Consul -Version 0.7.2.6

  • 自动注册服务到Consul,退出时取消注册。

如果是主动退出服务,会执行取消注册的方法,好处是比心跳响应更快,但是心跳也是必不可少的,因为在微服务中,各种网络原因都有可能导致服务异常中断。我这里是封装了一个方法来注册,代码如下所示:

public static void RegisterConsul(this IApplicationBuilder app,IApplicationLifetime lifetime, ServiceEntity serviceEntity)
{
var consulClient = new ConsulClient(x => x.Address = new Uri($"http://{serviceEntity.ConsulIP}:{serviceEntity.ConsulPort}"));//请求注册的 Consul 地址
var httpCheck = new AgentServiceCheck()
{
DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务启动多久后注册
Interval = TimeSpan.FromSeconds(10),//健康检查时间间隔,或者称为心跳间隔
HTTP = $"http://{serviceEntity.IP}:{serviceEntity.Port}/api/health",//健康检查地址
Timeout = TimeSpan.FromSeconds(5)
};

// Register service with consul
var registration = new AgentServiceRegistration()
{
Checks = new[] { httpCheck },
ID = Guid.NewGuid().ToString(),
Name = serviceEntity.ServiceName,
Address = serviceEntity.IP,
Port = serviceEntity.Port,
Tags = new[] { $"urlprefix-/{serviceEntity.ServiceName}" }//添加 urlprefix-/servicename 格式的 tag 标签,以便 Fabio 识别
};

consulClient.Agent.ServiceRegister(registration).Wait();

lifetime.ApplicationStopping.Register(() =>
{
consulClient.Agent.ServiceDeregister(registration.ID).Wait();//服务停止时取消注册
});
}

ID:正常环境中最好不要用guid,因为每次启动id都不一样,正常来讲没啥问题,但是快速启动时就会有个坑了。建议用,这样也是唯一的,并且不会重复。

ID=$"{serviceEntity.ServiceName}_{serviceEntity.IP}_{serviceEntity.Port}"
  • 修改Program

我想在用dotnet运行项目时,能够传入url,ip和port,所以在这里加了命令行参数。

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(build =>
{
build.AddCommandLine(args);//启动时可读取命令行参数
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});

  • 修改Configure方法,在最后加上RegisterConsul()

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, Microsoft.AspNetCore.Hosting.IApplicationLifetime lifetime)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

app.UseRouting();
app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});

//获取环境变量
var ip = Configuration.GetValue(typeof(String), "IP");
var port = Configuration.GetValue(typeof(String), "Port");
ServiceEntity serviceEntity = new ServiceEntity
{
IP = ip.ToString(),
Port = Convert.ToInt32(port),
ServiceName = Configuration["Service:Name"],
ConsulIP = Configuration["Consul:IP"],
ConsulPort = Convert.ToInt32(Configuration["Consul:Port"])
};

app.RegisterConsul(lifetime, serviceEntity);
}

  • 新建一个控制器,添加一个接口,用来测试。

[Route("api/[controller]")]
[ApiController]
public class DefaultController : ControllerBase
{
// GET: api/Default
[HttpGet]
public ResultInfo Get()
{
ResultInfo result = new ResultInfo();
result.Code = 200;
result.CreateTime = DateTime.Now;
result.Message = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}{HttpContext.Request.Path}";

result.Data = new {
name="zhanwei",
server="Api01"
};
return result;
}
}

  • 加上Health健康检查

    [Route("api/[controller]")]
[ApiController]
public class HealthController : ControllerBase
{
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "result", "ok" };
}
}

  • ServiceEntity实体类

    public class ServiceEntity
{
public string IP { get; set; }
public int Port { get; set; }
public string ServiceName { get; set; }
public string ConsulIP { get; set; }
public int ConsulPort { get; set; }
}

  • 修改配置文件,增加如下内容

  "Service": {
"Name": "Api01"//注册到Consul中显示的服务名称
},
"Consul": {
"IP": "192.168.10.134",//改成实际Consul地址
"Port": "8500"//Consul的端口
}

测试

  • 启动两个实例,只不过换一下端口号,我们看一下效果,用下面这个命令传入

dotnet CustomMiddleware.api01.dll --urls="http://*:5001/" --ip="192.168.10.134" --port="5001"

  • 如下图所示,我们起来了两个实例。 

  • 打开Consul网页客户端,我们看到Api01有两个实例 

  • 验证下两个服务的接口是否正常的?



    我们可以看到分别访问两个接口都是能正常返回的,这两个接口后面介绍网关负载均衡的时候还会用到,可以从返回的信息里区别是从哪个实例返回的。

再新建一个API服务

重复的过程就不再赘述了,我只是修改了下接口的路径,还有服务名称。

  • 启动一个实例 

  • 打开网页端,可以看到Api02也已经注册进去了。 

下期预告

现在就完成了服务的自动注册功能,其实光注册进去是没什么作用的,这只是为后续做准备而已,后面我会利用Ocelot组件做一个简单的网关,网关又是什么?

  • 服务自动发现、路由转发、负载均衡、限流、熔断等功能的讲解。

  • Consul单机版挂掉了不就完蛋了吗,所以后面还会介绍集群的搭建。

总之后面很精彩,这才刚起步而已。

文章转载自码农游乐场,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论