2.解决服务之间的通讯

作者

微信:tangy8080
电子邮箱:[email protected]
更新时间:2019-06-28 14:25:40 星期五

欢迎您订阅和分享我的订阅号,订阅号内会不定期分享一些我自己学习过程中的编写的文章
如您在阅读过程中发现文章错误,可添加我的微信 tangy8080 进行反馈.感谢您的支持。

文章主题

介绍多个服务之间进行通讯

前置条件

[无]

正文

服务之间应该尽量较少调用.以减少耦合度,如果彼此调用链过于频繁.可能会引起整个调用链的异常.
https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/architect-microservice-container-applications/communication-in-microservice-architecture
The microservice community promotes the philosophy of "smart endpoints and dumb pipes" This slogan encourages a design that‘s as decoupled as possible between microservices, and as cohesive as possible within a single microservice. As explained earlier, each microservice owns its own data and its own domain logic. But the microservices composing an end-to-end application are usually simply choreographed by using REST communications rather than complex protocols such as WS-* and flexible event-driven communications instead of centralized business-process-orchestrators.

但服务之间的调用有时候会变得"随其自然",以上一章利用consul实现k8s服务自动发现
结尾的两个服务为例说明

  • configcenter 作为整个集群甚至对外(这个时候,我们牵涉到一个概念:集中授权中兴 如果不授权,那集群外部访问配置.岂不是变得非常危险了吗?该问题我们会在后面提及)的配置中心,(它以consul的KV为存储)提供统一的配置
  • terminal 作为终端服务,它提供对集群外部终端的集中管控,例如通知终端更新,向终端发送通知等等

terminal需要数据库的配置信息(ip,port等)以存储各个终端的数据,这些配置存放在configcenter中.那么terminal服务如何从configcenter中取出配置呢? 这时候就涉及到了服务间的通讯了

服务之间如何通讯

凡是涉及到通讯的,一般都会涉及到两个概念

  • 通讯协议 TCP,UDP,HTTP等?
  • 数据锲约 JSON,XML又或者类似根据服务端协议自动生成客户端代码
通讯协议

在微服务之间通讯目前比较流行的有两种 TCP和HTTP

  • 在TCP通讯的基础上有一些框架可供您选择:Thrift,GRPC等
  • HTTP

    基于Consul和HTTP协议手撸一个微服务之间的库

    这里,我选择自己手撸一个简单的微服务之间的调用的库,它的工作模式如下

在ConsulCaller中,我们使用了Consul库进行服务发现.当发现了服务实例时,程序会根据随机算法选取实例.然后返回给调用方.

源码地址

git://gitblit.honeysuckle.site/public/Honeysuckle.git

实现方式讲解
  1. 扩展IServiceCollection,增加扩展函数AddConsulCaller.该函数通过调用方传入Consul的配置信息来进行服务配置.
    IConsulConfiguration 中定义了Consul的配置参数,包括consul的地址,端口,Token 由于简单这里不过多说明.
 public static void AddConsulCaller(this IServiceCollection services, Action<ConsulCallerOptions> optionsAction)
        {
            var consulConfiguration=new ConsulConfiguration();
            services.AddSingleton<IConsulConfiguration>(consulConfiguration);
            services.AddSingleton<IServiceDiscover, ServiceDiscover>();

            services.AddSingleton<IConsulClientFactory, ConsulClientFactory.ConsulClientFactory>();
            services.AddTransient<IConsulCaller, ConsulCaller>();

            var consulCallerOptions = new ConsulCallerOptions(consulConfiguration);
            optionsAction.Invoke(consulCallerOptions);
        }
  1. 我定义了一个IConsulClientFactory接口,它包含一个Get函数.用于返回IConsulClient对象.该对象支持发现consul中的服务实例
 public IConsulClient Get(IConsulConfiguration config)
        {
            return new ConsulClient(c =>
            {
                c.Address = new Uri($"http://{config.Host}:{config.Port}");

                if (!string.IsNullOrEmpty(config?.Token))
                {
                    c.Token = config.Token;
                }
            });
        }

3.另外一个重要的接口是IServiceDiscover,它包含一个GetServices的函数,该函数根据服务名称返回服务实例列表

        private readonly IConsulClient _consul;
        private const string VersionPrefix = "version-";

        public ServiceDiscover(IConsulClientFactory consulClientFactory, IConsulConfiguration consulConfiguration)
        {
            _consul = consulClientFactory.Get(consulConfiguration);
        }

        public  List<Service> GetServices(string key)
        {
            var queryResult =  _consul.Health.Service(key, string.Empty, true).Result;

            var services = new List<Service>();
            foreach (var serviceEntry in queryResult.Response)
            {
                if (IsValid(serviceEntry))
                {
                    var nodes =  _consul.Catalog.Nodes().Result;
                    if (nodes.Response == null)
                    {
                        services.Add(BuildService(serviceEntry, null));
                    }
                    else
                    {
                        var serviceNode = nodes.Response.FirstOrDefault(n => n.Address == serviceEntry.Service.Address);
                        services.Add(BuildService(serviceEntry, serviceNode));
                    }
                }
                else
                {
                    Console.WriteLine($"Unable to use service Address: {serviceEntry.Service.Address} and Port: {serviceEntry.Service.Port} as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0");
                }
            }
            return services;
        }

        private static Service BuildService(ServiceEntry serviceEntry, Node serviceNode)
        {
            return new Service(
                serviceEntry.Service.Service,
                new ServiceHostAndPort(serviceNode == null ? serviceEntry.Service.Address : serviceNode.Name, serviceEntry.Service.Port),
                serviceEntry.Service.ID,
                GetVersionFromStrings(serviceEntry.Service.Tags),
                serviceEntry.Service.Tags ?? Enumerable.Empty<string>());
        }

        private static bool IsValid(ServiceEntry serviceEntry)
        {
            if (string.IsNullOrEmpty(serviceEntry.Service.Address) || serviceEntry.Service.Address.Contains("http://") || serviceEntry.Service.Address.Contains("https://") || serviceEntry.Service.Port <= 0)
            {
                return false;
            }

            return true;
        }

        private static string GetVersionFromStrings(IEnumerable<string> strings)
        {
            return strings
                ?.FirstOrDefault(x => x.StartsWith(VersionPrefix, StringComparison.Ordinal))
                .TrimStart(VersionPrefix);
        }

4.最后我们提供了一个IConsulCaller接口,它提供一个Call接口,输入服务名称和一个回调函数.

如何使用?

1.在asp.net core引用中添加了ConsulCaller服务

 //使用ConsulCaller服务
            services.AddConsulCaller(options =>
            {
                options.ConsulConfiguration.Host = Configuration["Consul:Host"];
                options.ConsulConfiguration.Port = Convert.ToInt32(Configuration["Consul:Port"]);
            });

2.调用集群中的其他服务

const string serviceName = "configcenter";
            _consulCaller.Call(serviceName, (endpoint, httpclient) =>
            {
                try
                {
                    var uri = $"http://{endpoint.DownstreamHost}:{endpoint.DownstreamPort}/Consul/Get";
                   //根据返回的服务实例,实现自己的调用逻辑
                }
                catch (Exception ex)
                {
                    var errorMsg = $"{nameof(TerminalServerDbContext)}.{nameof(OnConfiguring)} _consulCaller.Call Error ";
                    Console.WriteLine(errorMsg + ex.Message);
                    Logger.Error(errorMsg, ex);
                }
                finally
                {
                    httpclient.Dispose();
                }
            });
  • IConsulCaller 对象在第一步已经被注入到IServiceCollection容器中

引用链接

[无]

原文地址:https://www.cnblogs.com/gytangyao/p/11407232.html

时间: 2024-10-10 18:48:09

2.解决服务之间的通讯的相关文章

Spring Cloud Stream 进行服务之间的通讯

Spring Cloud Stream Srping cloud Bus的底层实现就是Spring Cloud Stream,Spring Cloud Stream的目的是用于构建基于消息驱动(或事件驱动)的微服务架构.Spring Cloud Stream本身对Spring Messaging.Spring Integration.Spring Boot Actuator.Spring Boot Externalized Configuration等模块进行封装(整合)和扩展,下面我们实现两个

Feign解决服务之间调用传递token

在单体服务中调用一个接口时需要在Head里加token直接放在head里就行了,Feign之间调用服务需要加token怎么办呢,解决办法实现RequestInterceptor接口. 1.在调用服务中新建FeignConfig类并实现RequestInterceptor接口,重写apply方法. public class FeignConfig implements RequestInterceptor {        @Override        public void apply(Re

Spring Cloud微服务安全实战_6-1_微服务之间的通讯安全之概述

到目前为止已经实现了一个基于微服务的,前后端分离(这里我用的jquery做的,并不是真的前后端分离,因为我不会vue和angular所以没用)的架构.在网关上做了限流.认证.审计.授权等安全机制,在前端应用上也做了SSO单点登录, 现在的架构存在的问题是: 1,在网关做限流. 在网关上做限流是有问题的,比如订单服务限流是100,库存服务限流也是100,订单服务又调了库存服务.如果网关上给订单转了100个请求,给库存转了100个请求,订单又调了库存,这时候库存就同时接到了200个请求,库存服务就可

微服务之间的通讯安全(四)-JWT优化之日志、错误处理、限流及JWT改造后执行流程梳理

前面我们已经完成了通过JWT的认证和授权的改造,可以看到我们的代码中没有认证和授权的过滤器(Filter)了,基本上由SpringSecurity的过滤器来接管了,接下来我们来看一下怎么在SpringSecurity的过滤器链上加上我们自己的逻辑,比如日志和限流. 1.在SpringSecurity过滤器链上添加审计过滤器 1.1.创建日志过滤器,因为我们根据我们之前审计机制的位置,要把日志过滤器放到认证之后,授权之前.认证的过滤器会把JWT令牌转化为Authentication,然后放到安全上

微服务之间的通讯安全(六)-Sentinel入门之注解及熔断降级

1.Sentinel注解支持 在学习熔断降级之前,我们先来看一下Sentinel的注解支持,我们使用spring-cloud-starter-alibaba-sentinel依赖,无需额外配置即可使用@SentinelResource注解定义资源. @SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项.常用属性如下: value:资源名称,必需项(不能为空): blockHandler / blockHandlerClass: blockHandle

unity3D中Socket链接与服务之间的通讯测试程序

Client.js: 1 var net=require('net'); 2 var client=net.connect({port:8124,host:'127.0.0.1'},function(){ 3     console.log('Success'); 4     client.write('world'); 5 6 }); 7 //客户端接收数据 8 client.on('data',function(data){ 9     console.log(data.toString()

Python 网络编程socket大全 用途---用于客户端和服务器端之间相互通讯

本章目录 一.什么是socket 二.为什么需要socket 三.socket的发展 四.python中的socket 五.基于TCP的socket 六.基于UDP的socket 六. 粘包问题详解 七.粘包的解决方案 八.socketserver实现并发通讯 **引入:为什么一定要先学习网络协议?** 之所以学习网络编程就是为了让我们的程序能够利用网络来传输数据,开发出C/S构架的应用程序 而网络的核心,就是协议,没有协议就没有互联网,我们要开发出C/S结构程序则必须遵循这些协议的标准! `就

C#.NET通过Socket实现平行主机之间网络通讯(含图片传输的Demo演示)

在程序设计中,涉及数据存储和数据交换的时候,不管是B/S还是C/S模式,都有这样一个概念:数据库服务器.这要求一台性能和配置都比较好的主机作为服务器,以满足数目众多的客户端进行频繁访问.但是对于一些数据交换的要求不主同,而且涉及到的通讯个体数目不多,如果还采用“一主机多客户机”的模式,便要求一台硬件配置良好而且软件上安装了相关数据服务软件,这样会造成硬件和软件上的很多不必要的成本,这时Socket在点对点的平行对象之间的网络通讯的优势就就发挥出来了. 其实对于Socket通讯来说,服务器和客户端

设置Hyper-V和VMware多个服务之间共存

原文:设置Hyper-V和VMware多个服务之间共存 这个方法是解决多个服务之间不能共存,下面相当于是以Hyper-V和VMware做例子,其他的也适用. 今天准备安装VMware Workstation 10,然后玩玩MAC OS. 没想到,淡定的我双击安装包准备安装,啪的一个大红叉!我去?! 错误内容大概就是:当前电脑已经安装了Hyper-V,这个产品就装不上了. Hyper-V?什么玩意儿?那是系统的,默认服务是不安装的,但是安装了Visual Studio之后,自动给安装上了. 那怎么