donet 微服务开发 学习-consul 消费端开发

目的介绍

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获取可用端口。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值