目的介绍
donet 微服务开发 学习
编写服务消费者
创建类库RestTools
创建消息返回类ResponseEntity.cs
public class ResponseEntity<T>
{
/// <summary>
/// 返回状态码
/// </summary>
public HttpStatusCode StatusCode { get; set; }
/// <summary>
/// 返回的json反序列化出来的对象
/// </summary>
public T Body { get; set; }
/// <summary>
/// 响应的报文头
/// </summary>
public HttpResponseHeader Headers { get; set; }
}
创建转发消息类RestTemplate.cs
public class RestTemplate
{
private String consulServerUrl;
public RestTemplate(String consulServerUrl)
{
this.consulServerUrl = consulServerUrl;
}
/// <summary>
/// 获取服务的一个IP地址
/// </summary>
/// <param name="serviceName">consul服务IP</param>
/// <returns></returns>
private async Task<String> ResolveRootUrlAsync(String serviceName)
{
using (var consulClient = new ConsulClient(c => c.Address = new Uri(consulServerUrl)))
{
var services = (await consulClient.Agent.Services()).Response;
var agentServices = services.Where(s => s.Value.Service.Equals(serviceName, StringComparison.InvariantCultureIgnoreCase)).Select(s => s.Value);
//TODO:注入负载均衡策略
var agentService = agentServices.ElementAt(Environment.TickCount % agentServices.Count());
//根据当前TickCount对服务器个数取模,“随机”取一个机器出来,避免“轮询”的负载均衡策略需要计数加锁问题
return agentService.Address + ":" + agentService.Port;
}
}
/// <summary>
/// //把http://apiservice1/api/values转换为http://192.168.1.1:5000/api/values
/// </summary>
private async Task<String> ResolveUrlAsync(String url)
{
Uri uri = new Uri(url);
String serviceName = uri.Host;//apiservice1
String realRootUrl = await ResolveRootUrlAsync(serviceName);
return uri.Scheme + "://" + realRootUrl + uri.PathAndQuery;
}
/// <summary>
/// Get请求转换
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url">请求地址</param>
/// <param name="requestHeaders">请求头</param>
/// <returns></returns>
public async Task<ResponseEntity<T>> GetForEntityAsync<T>(String url, HttpRequestHeaders requestHeaders = null)
{
using (HttpClient httpClient=new HttpClient())
{
HttpRequestMessage requestMsg = new HttpRequestMessage();
if (requestHeaders!=null)
{
foreach (var header in requestHeaders)
{
httpClient.DefaultRequestHeaders.Add(header.Key, header.Value);
}
}
requestMsg.Method = HttpMethod.Get;
//http://apiservice1/api/values转换为http://192.168.1.1:5000/api/values
requestMsg.RequestUri = new Uri(await ResolveUrlAsync(url));
var result = await httpClient.SendAsync(requestMsg);
ResponseEntity<T> responseEntity = new ResponseEntity<T>();
responseEntity.StatusCode = result.StatusCode;
String bodyStr = await result.Content.ReadAsStringAsync();
responseEntity.Body = JsonConvert.DeserializeObject<T>(bodyStr);
responseEntity.Headers = responseEntity.Headers;
return responseEntity;
}
}
}
编写控制台进行消费
static void Main(string[] args)
{
RestTemplate rest = new RestTemplate("http://127.0.0.1:8500");
//RestTemplate把服务的解析和发请求以及响应反序列化帮我们完成
ResponseEntity<String[]> resp = rest.GetForEntityAsync<String[]>("http://apiservice1/api/values").Result;
Console.WriteLine(resp.StatusCode);
Console.WriteLine(String.Join(",",resp.Body));
Console.ReadKey();
}
测试结果:
简化服务的注册
每次启动、注册服务都要指定一个端口,本地测试集群的时候可能要启动多个实例,很麻烦.
在ASP. Net Core中只要设定端口为0,那么服务器会随机找一个可用的端口绑定(测试一下).,但是没有找到读取到这个随机端口号的方法.因此自己写:
新建Tools.cs工具类
public class Tools
{
/// <summary>
/// 产生一个介于minPort-maxPort之间的随机可用端口
/// </summary>
/// <param name="minPort"></param>
/// <param name="maxPort"></param>
/// <returns></returns>
public static int GetRandAvailablePort(int minPort = 1024, int maxPort = 65535)
{
Random r = new Random();
while (true)
{
int port = r.Next(minPort, maxPort);
if (!IsPortInUsed(port))
{
return port;
}
}
}
/// <summary>
/// 判断port端口是否在使用中
/// </summary>
/// <param name="port"></param>
/// <returns></returns>
private static bool IsPortInUsed(int port)
{
IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
IPEndPoint[] ipsTCP = ipGlobalProperties.GetActiveTcpListeners();
if (ipsTCP.Any(p=>p.Port==port))
{
return true;
}
IPEndPoint[] ipsUDP = ipGlobalProperties.GetActiveUdpListeners();
if (ipsUDP.Any(p=>p.Port==port))
{
return true;
}
TcpConnectionInformation[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections();
if (tcpConnInfoArray.Any(conn=>conn.LocalEndPoint.Port==port))
{
return true;
}
return false;
}
}
使用方法
public static IWebHost BuildWebHost(string[] args)
{
var config = new ConfigurationBuilder()
.AddCommandLine(args)
.Build();
String ip = config["ip"];
String port = config["port"];
if (port=="0")
{
port = Tools.GetRandAvailablePort().ToString();
}
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseUrls($"http://{ip}:{port}")
.Build();
}
在程序启动的时候如果port=0或者没有指定port,则自己调用GetRandAvailablePort获取可用端口。