当前位置: 首页 > news >正文

深圳网站建设_网站模板去哪下载

深圳网站建设_,网站模板去哪下载,可信的手机网站建设,怎么做切片网站微服务 一、微服务#xff08;microservices#xff09; 近几年,微服这个词闯入了我们的视线范围。在百度与谷歌中随便搜一搜也有几千万条的结果。那么#xff0c;什么是微服务 呢#xff1f;微服务的概念是怎么产生的呢#xff1f; 我们就来了解一下Go语言与微服务的千丝…微服务 一、微服务microservices 近几年,微服这个词闯入了我们的视线范围。在百度与谷歌中随便搜一搜也有几千万条的结果。那么什么是微服务 呢微服务的概念是怎么产生的呢 我们就来了解一下Go语言与微服务的千丝万缕与来龙去脉。 什么是微服务 在介绍微服务时首先得先理解什么是微服务顾名思义微服务得从两个方面去理解什么是微、什么是服 务 微micro 狭义来讲就是体积小著名的2 pizza 团队很好的诠释了这一解释2 pizza 团队最早是亚马逊 CEO Bezos提出来的意思是说单个服务的设计所有参与人从设计、开发、测试、运维所有人加起来 只需要2个披萨 就够了 。 服务service 一定要区别于系统服务一个或者一组相对较小且独立的功能单元是用户可以感知最小功能 集。 那么广义上来讲微服务是一种分布式系统解决方案推动细粒度服务的使用这些服务协同工作。 微服务这个概念的由来 据说早在2011年5月在威尼斯附近的软件架构师讨论会上就有人提出了微服务架构设计的概念用它来描述 与会者所见的一种通用的架构设计风格。时隔一年之后在同一个讨论会上大家决定将这种架构设计风格用微服 务架构来表示。 起初对微服务的概念没有一个明确的定义大家只能从各自的角度说出了微服务的理解和看法。 有人把微服务 理解为一种细粒度SOAservice-oriented Architecture面向服务架构一种轻量级的组件化的小型SOA。 在2014年3月詹姆斯·刘易斯James Lewis与马丁·福勒Martin Fowler所发表的一篇博客中总结了微服 务架构设计的一些共同特点这应该是一个对微服务比较全面的描述。 原文链接 https://martinfowler.com/articles/microservices.html这篇文章中认为“简而言之微服务架构风格是将单个应用程序作为一组小型服务开发的方法每个服务程序都 在自己的进程中运行并与轻量级机制通常是HTTP资源API进行通信。这些服务是围绕业务功能构建的。可以 通过全自动部署机器独立部署。这些服务器可以用不同的编程语言编写使用不同的数据存储技术并尽量不用集 中式方式进行管理” 微服务与微服务框架 在这里我们可能混淆了一个点那就是微服务和微服务架构这应该是两个不同的概念而我们平时说到的微服务 可能就已经包含了这两个概念了所以我们要把它们说清楚以免我们很纠结。微服务架构是一种设计方法而微服 务这是应该指使用这种方法而设计的一个应用。所以我们必要对微服务的概念做出一个比较明确的定义。 微服务框架是将复杂的系统使用组件化的方式进行拆分并使用轻量级通讯方式进行整合的一种设计方法。 微服务是通过这种架构设计方法拆分出来的一个独立的组件化的小应用。 微服务架构定义的精髓可以用一句话来描述那就是“分而治之合而用之”。 将复杂的系统进行拆分的方法就是“分而治之”。分而治之可以让复杂的事情变的简单这很符合我们平时处理 问题的方法。 使用轻量级通讯等方式进行整合的设计就是“合而用之”的方法合而用之可以让微小的力量变动强大。 微服务架构和整体式架构的区别 开发单体式整体式应用的不足之处 三层架构MVC的具体内容如下 表示层view 用户使用应用程序时看到的、听见的、输入的或者交互的部分。 业务逻辑层controller 根据用户输入的信息进行逻辑计算或者业务处理的部分。 数据访问层model 关注有效地操作原始数据的部分如将数据存储到存储介质如数据库、文件系统及 从存储介质中读取数据等。 虽然现在程序被分成了三层但只是逻辑上的分层并不是物理上的分层。也就是说对不同层的代码而言经过 编译、打包和部署后所有的代码最终还是运行在同一个进程中。而这就是所谓的单块架构。 单体架构在规模比较小的情况下工作情况良好但是随着系统规模的扩大它暴露出来的问题也越来越多主要有 以下几点 复杂性逐渐变高 比如有的项目有几十万行代码各个模块之间区别比较模糊逻辑比较混乱代码越多复杂性越高越难解决遇到 的问题。 技术债务逐渐上升 公司的人员流动是再正常不过的事情有的员工在离职之前疏于代码质量的自我管束导致留下来很多坑由于 单体项目代码量庞大的惊人留下的坑很难被发觉这就给新来的员工带来很大的烦恼人员流动越大所留下的坑 越多也就是所谓的技术债务越来越多。 维护成本大 当应用程序的功能越来越多、团队越来越大时沟通成本、管理成本显著增加。当出现 bug 时可能引起 bug 的 原因组合越来越多导致分析、定位和修复的成本增加并且在对全局功能缺乏深度理解的情况下容易在修复 bug 时引入新的 bug。 持续交付周期长 构建和部署时间会随着功能的增多而增加任何细微的修改都会触发部署流水线。新人培养周期长新成员了解背 景、熟悉业务和配置环境的时间越来越长。 技术选型成本高 单块架构倾向于采用统一的技术平台或方案来解决所有问题如果后续想引入新的技术或框架成本和风险都很 大。 可扩展性差 随着功能的增加垂直扩展的成本将会越来越大而对于水平扩展而言因为所有代码都运行在同一个进程没办 法做到针对应用程序的部分功能做独立的扩展。 微服务架构的特性 单一职责 微服务架构中的每个服务都是具有业务逻辑的符合高内聚、低耦合原则以及单一职责原则的单元不同的服务 通过“管道”的方式灵活组合从而构建出庞大的系统。 轻量级通信(跨语言) 服务之间通过轻量级的通信机制实现互通互联而所谓的轻量级通常指语言无关、平台无关的交互方式。 对于轻量级通信的格式而言我们熟悉的 XML 和 JSON它们是语言无关、平台无关的对于通信的协议而言通 常基于 HTTP能让服务间的通信变得标准化、无状态化。目前大家熟悉的 RESTRepresentational State Transfer是实现服务间互相协作的轻量级通信机制之一。使用轻量级通信机制可以让团队选择更适合的语言、 工具或者平台来开发服务本身。 问REST是什么和restful一样吗 答REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。 独立性 每个服务在应用交付过程中独立地开发、测试和部署。 在单块架构中所有功能都在同一个代码库功能的开发不具有独立性当不同小组完成多个功能后需要经过集成 和回归测试测试过程也不具有独立性当测试完成后应用被构建成一个包如果某个功能存在 bug将导致整 个部署失败或者回滚。 在微服务架构中每个服务都是独立的业务单元与其他服务高度解耦只需要改变当前服务本身就可以完成独 立的开发、测试和部署。 进程隔离 单块架构中整个系统运行在同一个进程中当应用进行部署时必须停掉当前正在运行的应用部署完成后再重 启进程无法做到独立部署。 有时候我们会将重复的代码抽取出来封装成组件在单块架构中组件通常的形态叫做共享库如 jar 包或者 DLL但是当程序运行时所有组件最终也会被加载到同一进程中运行。 在微服务架构中应用程序由多个服务组成每个服务都是高度自治的独立业务实体可以运行在独立的进程中 不同的服务能非常容易地部署到不同的主机上。 微服务架构的缺点 运维要求较高 对于单体架构来讲我们只需要维护好这一个项目就可以了但是对于微服务架构来讲由于项目是由多个微服务 构成的每个模块出现问题都会造成整个项目运行出现异常想要知道是哪个模块造成的问题往往是不容易的因 为我们无法一步一步通过debug的方式来跟踪这就对运维人员提出了很高的要求。 分布式的复杂性 对于单体架构来讲我们可以不使用分布式但是对于微服务架构来说分布式几乎是必会用的技术由于分布式 本身的复杂性导致微服务架构也变得复杂起来。 接口调整成本高 比如用户微服务是要被订单微服务和电影微服务所调用的一旦用户微服务的接口发生大的变动那么所有依赖 它的微服务都要做相应的调整由于微服务可能非常多那么调整接口所造成的成本将会明显提高。 重复劳动 对于单体架构来讲如果某段业务被多个模块所共同使用我们便可以抽象成一个工具类被所有模块直接调用 但是微服务却无法这样做因为这个微服务的工具类是不能被其它微服务所直接调用的从而我们便不得不在每个 微服务上都建这么一个工具类从而导致代码的重复。 为什么使用微服务架构 开发简单 微服务架构将复杂系统进行拆分之后让每个微服务应用都开放变得非常简单没有太多的累赘。对于每一个开发 者来说这无疑是一种解脱因为再也不用进行繁重的劳动了每天都在一种轻松愉快的氛围中工作其效率也会 成备地提高 快速响应需求变化 一般的需求变化都来自于局部功能的改变这种变化将落实到每个微服务上每个微服务的功能相对来说都非常 简单更改起来非常容易所有微服务非常适合敏捷开发方法能够快速的影响业务的需求变化。 随时随地更新 一方面微服务的部署和更新并不会影响全局系统的正常运行另一方面使用多实例的部署方法可以做到一个 服务的重启和更新在不易察觉的情况下进行。所以每个服务任何时候都可以进行更新部署。 系统更加稳定可靠 微服务运行在一个高可用的分布式环境之中有配套的监控和调度管理机制并且还可以提供自由伸缩的管理充 分保障了系统的稳定可靠性 二、Protobuf protobuf是google旗下的一款平台无关语言无关可扩展的序列化结构数据格式。所以很适合用做数据存储和作 为不同应用不同语言之间相互通信的数据交换格式只要实现相同的协议格式即同一 proto文件被编译成不同的 语言版本加入到各自的工程中去。这样不同语言就可以解析其他语言通过 protobuf序列化的数据。目前官网提 供了 C,Python,JAVA,GO等语言的支持。google在2008年7月7号将其作为开源项目对外公布。 protoBuf简介 Google Protocol Buffer(简称 Protobuf)是一种轻便高效的结构化数据存储格式平台无关、语言无关、可扩展 可用于通讯协议和数据存储等领域。 数据交互的格式比较 数据交互xml、json、protobuf格式比较 1、json: 一般的web项目中最流行的主要还是json。因为浏览器对于json数据支持非常好有很多内建的函数支 持。 2、xml: 在webservice中应用最为广泛但是相比于json它的数据更加冗余因为需要成对的闭合标签。json使 用了键值对的方式不仅压缩了一定的数据空间同时也具有可读性。 3、protobuf:是后起之秀是谷歌开源的一种数据格式适合高性能对响应速度有要求的数据传输场景。因为 profobuf是二进制数据格式需要编码和解码。数据本身不具有可读性。因此只能反序列化之后得到真正可读的数 据。 相对于其它protobuf更具有优势 1序列化后体积相比Json和XML很小适合网络传输 2支持跨平台多语言 3消息格式升级和兼容性还不错 4序列化反序列化速度很快快于Json的处理速度 protoBuf的优点 Protobuf 有如 XML不过它更小、更快、也更简单。你可以定义自己的数据结构然后使用代码生成器生成的代 码来读写这个数据结构。你甚至可以在无需重新部署程序的情况下更新数据结构。只需使用 Protobuf 对数据结构 进行一次描述即可利用各种不同语言或从各种不同数据流中对你的结构化数据轻松读写。 它有一个非常棒的特性即“向后”兼容性好人们不必破坏已部署的、依靠“老”数据格式的程序就可以对数据结构 进行升级。 Protobuf 语义更清晰无需类似 XML 解析器的东西因为 Protobuf 编译器会将 .proto 文件编译生成对应的数据 访问类以对 Protobuf 数据进行序列化、反序列化操作。使用 Protobuf 无需学习复杂的文档对象模型 Protobuf 的编程模式比较友好简单易学同时它拥有良好的文档和示例对于喜欢简单事物的人们而言 Protobuf 比其他的技术更加有吸引力。 ProtoBuf 的不足 Protobuf 与 XML 相比也有不足之处。它功能简单无法用来表示复杂的概念。 XML 已经成为多种行业标准的编写工具Protobuf 只是 Google 公司内部使用的工具在通用性上还差很多。 由 于文本并不适合用来描述数据结构所以 Protobuf 也不适合用来对基于文本的标记文档如 HTML建模。另 外由于 XML 具有某种程度上的自解释性它可以被人直接读取编辑在这一点上 Protobuf 不行它以二进制的 方式存储除非你有 .proto 定义否则你没法直接读出 Protobuf 的任何内容。 Protobuf安装 安装protoBuf #下载 protoBuf $ git clone https://github.com/protocolbuffers/protobuf.git--这个下载并没有autogen.sh #或者直接将压缩包拖入后解压 unzip protobuf.zip--libprotoc 3.5.1 #安装依赖库 $ sudo apt-get install autoconf automake libtool curl make g unzip libffi-dev -y--可以找我要zip包 #安装 $ cd protobuf/ $ ./autogen.sh $ ./configure $ make $ sudo make install $ sudo ldconfig # 刷新共享库 很重要的一步啊 #安装的时候会比较卡 #成功后需要使用命令测试 $ protoc –h获取 proto包 #Go语言的proto API接口 $ go get -v -u github.com/golang/protobuf/proto--下载不下来--将压缩包放到go里面去 chengpengchengpeng:/home/jian/share/src/github.com/golang/protobuf$ cd protoc-gen-go/ chengpengchengpeng:/home/jian/share/src/github.com/golang/protobuf/protoc-gen-go$ ls descriptor doc.go generator golden_test.go grpc link_grpc.go main.go plugin testdata itcastitcast:/home/jian/share/src/github.com/golang/protobuf/protoc-gen-go$ go build error obtaining VCS status: exit status 128Use -buildvcsfalse to disable VCS stamping. chengpengchengpeng:/home/jian/share/src/github.com/golang/protobuf/protoc-gen-go$ go env -w GOFLAGS-buildvcsfalse chengpengchengpeng:/home/jian/share/src/github.com/golang/protobuf/protoc-gen-go$ go build chengpengchengpeng:/home/jian/share/src/github.com/golang/protobuf/protoc-gen-go$ ls descriptor doc.go generator golden_test.go grpc link_grpc.go main.go plugin protoc-gen-go testdata chengpengchengpeng:/home/jian/share/src/github.com/golang/protobuf/protoc-gen-go$ 得到一个protoc-gen-go可执行文件 chengpengchengpeng:/home/jian/share/src/github.com/golang/protobuf/protoc-gen-go$ sudo mv protoc-gen-go /bin/ 测试使用protoc-gen-go这个补全就OK了安装protoc-gen-go插件 它是一个 go程序编译它之后将可执行文件复制到\bin目录。 #安装 $ go get -v -u github.com/golang/protobuf/protoc-gen-go #编译 $ cd $GOPATH/src/github.com/golang/protobuf/protoc-gen-go/ $ go build #将生成的 protoc-gen-go可执行文件放在/bin目录下 $ sudo cp protoc-gen-go /bin/protobuf的语法 要想使用 protobuf必须得先定义 proto文件。所以得先熟悉 protobuf的消息定义的相关语法。 定义一个消息类型 syntax proto3;--proto3版本 message PandaRequest {--发送的请求 string name 1; int32 shengao 2; repeated int32 tizhong 3;--表示重复的那么在go语言中用切片进行代表 }PandaRequest消息格式有3个字段在消息中承载的数据分别对应于每一个字段。其中每个字段都有一个名字和一种类型。 文件的第一行指定了你正在使用proto3语法如果你没有指定这个编译器会使用proto2。这个指定语法行必须是文件的非空非注释的第一个行。 在上面的例子中所有字段都是标量类型两个整型shengao和tizhong一个string类型name。 Repeated 关键字表示重复的那么在go语言中用切片进行代表 正如上述文件格式在消息定义中每个字段都有唯一的一个标识符。 添加更多消息类型 在一个.proto文件中可以定义多个消息类型。在定义多个相关的消息的时候这一点特别有用——例如如果想定义与SearchResponse消息类型对应的回复消息格式的话你可以将它添加到相同的.proto文件中 syntax proto3; message PandaRequest { string name 1; int32 shengao 2; int32 tizhong 3; } message PandaResponse { ... }添加注释 向.proto文件添加注释可以使用C/C/java/Go风格的双斜杠// 语法格式如 syntax proto3; message PandaRequest { string name 1; //姓名 int32 shengao 2; //身高 int32 tizhong 3; //体重 } message PandaResponse { ... }从.proto文件生成了什么 当用protocol buffer编译器来运行.proto文件时编译器将生成所选择语言的代码这些代码可以操作在.proto文件中定义的消息类型包括获取、设置字段值将消息序列化到一个输出流中以及从一个输入流中解析消息。 对C来说编译器会为每个.proto文件生成一个.h文件和一个.cc文件.proto文件中的每一个消息有一个对应的类。 对Python来说有点不太一样——Python编译器为.proto文件中的每个消息类型生成一个含有静态描述符的模 块该模块与一个元类metaclass在运行时runtime被用来创建所需的Python数据访问类。 对go来说编译器会为每个消息类型生成了一个.pd.go文件。 标准数据类型 一个标量消息字段可以含有一个如下的类型——该表格展示了定义于.proto文件中的类型以及与之对应的、在自动生成的访问类中定义的类型 如果不好用可以全部转成[]byte类型 默认值 当一个消息被解析的时候如果被编码的信息不包含一个特定的元素被解析的对象锁对应的域被设置位一个默认 值对于不同类型指定如下 对于strings默认是一个空string 对于bytes默认是一个空的bytes 对于bools默认是false 对于数值类型默认是0 使用其他消息类型 你可以将其他消息类型用作字段类型。例如假设在每一个PersonInfo消息中包含Person消息此时可以在相同 的.proto文件中定义一个Person消息类型然后在PersonInfo消息中指定一个Person类型的字段 message PersonInfo { repeated Person info 1; } message Person { string name 1; int32 shengao 2; repeated int32 tizhong 3; }使用proto2消息类型 在你的proto3消息中导入proto2的消息类型也是可以的反之亦然然后proto2枚举不可以直接在proto3的标识 符中使用如果仅仅在proto2消息中使用是可以的。 嵌套类型 你可以在其他消息类型中定义、使用消息类型在下面的例子中Person消息就定义在PersonInfo消息内如 message PersonInfo {message Person {string name 1;int32 shengao 2;repeated int32 tizhong 3;}repeated Person info 1; }如果你想在它的父消息类型的外部重用这个消息类型你需要以PersonInfo.Person的形式使用它如 message PersonMessage {PersonInfo.Person info 1; }当然你也可以将消息嵌套任意多层如 message Grandpa { // Level 0message Father { // Level 1message son { // Level 2string name 1;int32 age 2;}}message Uncle { // Level 1message Son { // Level 2string name 1;int32 age 2;}} }定义服务(Service) 如果想要将消息类型用在RPC(远程方法调用)系统中可以在.proto文件中定义一个RPC服务接口protocol buffer编译器将会根据所选择的不同语言生成服务接口代码及存根。如想要定义一个RPC服务并具有一个方法该方法能够接收 SearchRequest并返回一个SearchResponse此时可以在.proto文件中进行如下定义 service SearchService { //rpc 服务的函数名 传入参数返回返回参数 rpc Search (SearchRequest) returns (SearchResponse); }最直观的使用protocol buffer的RPC系统是gRPC一个由谷歌开发的语言和平台中的开源的RPC系统gRPC在使用protocl buffer时非常有效如果使用特殊的protocol buffer插件可以直接为您从.proto文件中产生相关的RPC代码。 如果你不想使用gRPC也可以使用protocol buffer用于自己的RPC实现你可以从proto2语言指南中找到更多信息 生成访问类了解 可以通过定义好的.proto文件来生成Java,Python,C, Ruby, JavaNano, Objective-C,或者C# 代码需要基于.proto文件运行protocol buffer编译器protoc。如果你没有安装编译器下载安装包并遵照README安装。对于Go,你还需要安装一个特殊的代码生成器插件。你可以通过GitHub上的protobuf库找到安装过程 通过如下方式调用protocol编译器 protoc --proto_pathIMPORT_PATH --cpp_outDST_DIR --python_outDST_DIR --go_outDST_DIR path/to/file.protoIMPORT_PATH(指定相对于的目录)声明了一个.proto文件所在的解析import具体目录。如果忽略该值则使用当前目录。如果有多个目录则可以多次调用–proto_path它们将会顺序的被访问并执行导入。-IIMPORT_PATH是–proto_path的简化形式—这种方式一般不用。 当然也可以提供一个或多个输出路径 –cpp_out 在目标目录DST_DIR中产生C代码可以在C代码生成参考中查看更多。 –python_out 在目标目录 DST_DIR 中产生Python代码可以在Python代码生成参考中查看更多。 –go_out 在目标目录 DST_DIR 中产生Go代码可以在GO代码生成参考中查看更多。 作为一个方便的拓展如果DST_DIR以.zip或者.jar结尾编译器会将输出写到一个ZIP格式文件或者符合JAR标准的.jar文件中。注意如果输出已经存在则会被覆盖编译器还没有智能到可以追加文件。 你必须提议一个或多个.proto文件作为输入多个.proto文件可以只指定一次。虽然文件路径是相对于当前目录的每个文件必须位于其IMPORT_PATH下以便每个文件可以确定其规范的名称。 测试 protobuf的使用方法是将数据结构写入到 .proto文件中使用 protoc编译器编译(间接使用了插件得到一个新的go包里面包含 go中可以使用的数据结构和一些辅助方法。 编写 test.proto文件 1.$GOPATH/src/创建 myproto文件夹 $ cd $GOPATH/src/ $ make myproto2.myproto文件夹中创建 test.proto文件 (protobuf协议文件 $ vim test.proto文件内容 syntax proto3; package myproto; message Test { string name 1; int32 stature 2 ; repeated int64 weight 3; string motto 4; }3.编译 :执行 $ protoc --go_out./ *.proto生成 test.pb.go文件 4.使用 protobuf做数据格式转换 package main import ( fmt github.com/golang/protobuf/proto myproto ) func main() { test : myproto.Test{ Name : panda, Stature : 180, Weight : []int64{120,125,198,180,150,180}, Motto : 天行健地势坤, } //将Struct test 转换成 protobuf data,err: proto.Marshal(test) if err!nil{ fmt.Println(转码失败,err) } //得到一个新的Test结构体 newTest newtest: myproto.Test{} //将data转换为test结构体 err proto.Unmarshal(data,newtest) if err!nil { fmt.Println(转码失败,err) } fmt.Println(newtest.String()) //得到name字段 fmt.Println(newtest-name,newtest.GetName()) fmt.Println(newtest-Stature,newtest.GetStature()) fmt.Println(newtest-Weight,newtest.GetWeight()) fmt.Println(newtest-Motto,newtest.GetMotto()) }三、GRPC gRPC 是一个高性能、开源和通用的 RPC 框架面向移动和 HTTP/2 设计。 gRPC基于 HTTP/2标准设计带来诸如双向流、流控、头部压缩、单 TCP连接上的多复用请求等特。这些特性使得其在移动设备上表现更好更省电和节省空间占用 RPC RPCRemote Procedure Call Protocol——远程过程调用协议它是一种通过网络从远程计算机程序上请求服务而不需要了解底层网络技术的协议。 简单来说就是跟远程访问或者web请求差不多都是一个client向远端服务器请求服务返回结果但是web请求使用的网络协议是http高层协议而rpc所使用的协议多为TCP是网络层协议减少了信息的包装加快了处理速度。 golang本身有rpc包可以方便的使用来构建自己的rpc服务下边是一个简单是实例可以加深我们的理解 1.调用客户端句柄执行传送参数 2.调用本地系统内核发送网络消息 3.消息传送到远程主机 4.服务器句柄得到消息并取得参数 5.执行远程过程 6.执行的过程将结果返回服务器句柄 7.服务器句柄返回结果调用远程系统内核 8.消息传回本地主机 9.客户句柄由内核接收消息 10.客户接收句柄返回的数据 服务端 package main import ( net/http net/rpc net github.com/astaxie/beego io ) //- 方法是导出的 //- 方法有两个参数都是导出类型或内建类型 //- 方法的第二个参数是指针 //- 方法只有一个error接口类型的返回值 // //func (t *T) MethodName(argType T1, replyType *T2) error type Panda int; func (this *Panda)Getinfo(argType int, replyType *int) error { beego.Info(argType) *replyType 1 argType return nil } func main() { //注册1个页面请求 http.HandleFunc(/panda,pandatext) //new 一个对象 pd :new(Panda) //注册服务 //Register在默认服务中注册并公布 接收服务 pd对象 的方法 rpc.Register(pd) rpc.HandleHTTP() //建立网络监听 ln , err :net.Listen(tcp,127.0.0.1:10086) if err ! nil{ beego.Info(网络连接失败) } beego.Info(正在监听10086) //service接受侦听器l上传入的HTTP连接 http.Serve(ln,nil) } //用来现实网页的web函数 func pandatext(w http.ResponseWriter, r *http.Request) { io.WriteString(w,panda) }服务端案例 package mainimport (fmtionetnet/httpnet/rpc )// 用来现实网页的web函数 func pandatext(w http.ResponseWriter, r *http.Request) {io.WriteString(w, hello world) }/*- 方法是导出的- 方法有两个参数都是导出类型或内建类型- 方法的第二个参数是指针- 方法只有一个error接口类型的返回值 */ //func (t *T) MethodName(argType T1, replyType *T2) errortype Panda int //这是一个对象func (this *Panda) GetInfo(argType int, replyType *int) error {fmt.Println(打印对端发送过来的内容:, argType)//修改内容值*replyType argType 12306return nil } func main() {//页面请求http.HandleFunc(/chengpeng, pandatext)//服务端注册一个对象 //new 一个对象pd : new(Panda)//Register在默认服务中注册并公布 接收服务 pd对象 的方法rpc.Register(pd) //这样就会暴露出来//HandleHTTP函数注册DefaultServer的RPC信息HTTP处理器对应到DefaultRPCPath//和DefaultServer的debug处理器对应到DefaultDebugPathrpc.HandleHTTP()//建立网络连接listen, err : net.Listen(tcp, :10086)if err ! nil {fmt.Println(链接错误)}http.Serve(listen, nil) }客户端 package main import ( net/rpc github.com/astaxie/beego ) func main() { //rpc的与服务端建立网络连接 cli,err : rpc.DialHTTP(tcp,127.0.0.1:10086) if err !nil { beego.Info(网络连接失败) } var val int //远程调用函数被调用的方法传入的参数 返回的参数 err cli.Call(Panda.Getinfo,123,val) if err!nil{ beego.Info(打call失败) } beego.Info(返回结果,val) }客户端案例 .package mainimport (fmtnet/rpc )func main() {//建立网络连接client, err : rpc.DialHTTP(tcp, 127.0.0.1:10086)if err ! nil {fmt.Println(网络连接失败, err)return}var pd int//远程调用函数被调用的方法传入的参数 返回的参数err client.Call(Panda.GetInfo, 12307, pd)if err ! nil {fmt.Println(打call失败)return}fmt.Println(最后打印结果:, pd) }GRPC是什么 在 gRPC里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法使得您能够更容易地创建分布式应用和服务。与许多 RPC系统类似 gRPC也是基于以下理念 定义一个服务指定其能够被远程调用的方法包含参数和返回类型。 在服务端实现这个接口并运行一个 gRPC服务器来处理客户端调用。 在客户端拥有一个存根能够像服务端一样的方法。 gRPC客户端和服务端可以在多种环境中运行和交互 -从 google内部的服务器到你自己的笔记本并且可以用任何 gRPC支持的语言 来编写。 所以你可以很容易地用 Java创建一个 gRPC服务端用 Go、 Python、Ruby来创建客户端。此外 Google最新API将有 gRPC版本的接口使你很容易地将 Google的功能集成到你的应用里。 GRPC使用 protocol buffers gRPC默认使用protoBuf这是 Google开源的一套成熟的结构数据序列化机制当然也可以使用其他数据格式如JSON。正如你将在下方例子里所看到的你用 proto files创建 gRPC服务用 protoBuf消息类型来定义方法参数和返回类型。你可以在 Protocol Buffers文档找到更多关于 protoBuf的资料。 虽然你可以使用 proto2 (当前默认的 protocol buffers版本 )我们通常建议你在 gRPC里使用 proto3因为这样你可以使用 gRPC支持全部范围的的语言并且能避免 proto2客户端与 proto3服务端交互时出现的兼容性问题反之亦然。 你好 gRPC 现在你已经对 gRPC有所了解了解其工作机制最简单的方法是看一个简单的例子。 Hello World将带领你创建一个简单的客户端 —— 服务端应用向你展示 通过一个protoBuf模式定义一个简单的带有 Hello World方法的 RPC服务。 用你最喜欢的语言 (如果可用的话 )来创建一个实现了这个接口的服务端。 用你最喜欢的 (或者其他你愿意的 )语言来访问你的服务端。 这个例子完整的代码在我们 GitHub源码库的 examples目录下。我们使用 Git版本系统来进行源码管理但是除了如何安装和运行一些 Git命令外你没必要知道其他关于 Git的任何事情。需要注意的是并不是所有 gRPC支持的语言都可以编写我们例子的服务端代码比如 PHP和 Objective-C仅支持创建客户端。比起针对于特定语言的复杂教程这更像是一个介绍性的例子。你可以在本站找到更有深度的教程gRPC支持的语言的参考文档很快就会全部开放。 环境搭建 #将x.zip 解压到 $GOPATH/src/golang.org/x 目录下 $ unzip x.zip -d /GOPATH/src/golang.org/x #-d 是指定解压目录地址 #/home/chengpeng/go/src/golang.org #文件名为x #将google.golang.org.zip 解压到 $GOPATH/src/google.golang.org 目录下启动服务端 $ cd $GOPATH/src/google.golang.org/grpc/examples/helloworld/greeter_server $ go run main.go启动客户端 $ cd $GOPATH/src/google.golang.org/grpc/examples/helloworld/greeter_client $ go run main.go客户端代码介绍 package main import ( log os golang.org/x/net/context google.golang.org/grpc pb google.golang.org/grpc/examples/helloworld/helloworld //这是引用编译好的protobuf ) const ( address localhost:50051 defaultName world ) func main() { // 建立到服务器的连接。 conn, err : grpc.Dial(address, grpc.WithInsecure()) if err ! nil { log.Fatalf(did not connect: %v, err) } //延迟关闭连接 defer conn.Close() //调用protobuf的函数创建客户端连接句柄 c : pb.NewGreeterClient(conn) // 联系服务器并打印它的响应。 name : defaultName if len(os.Args) 1 { name os.Args[1] } //调用protobuf的sayhello函数 r, err : c.SayHello(context.Background(), pb.HelloRequest{Name: name}) if err ! nil { log.Fatalf(could not greet: %v, err) } //打印结果 log.Printf(Greeting: %s, r.Message) }服务端代码介绍 package main import ( log net golang.org/x/net/context google.golang.org/grpc pb google.golang.org/grpc/examples/helloworld/helloworld google.golang.org/grpc/reflection ) const ( port :50051 ) // 服务器用于实现helloworld.GreeterServer。 type server struct{} // SayHello实现helloworld.GreeterServer func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { return pb.HelloReply{Message: Hello in.Name}, nil } func main() { //监听 lis, err : net.Listen(tcp, port) if err ! nil { log.Fatalf(failed to listen: %v, err) } //new服务对象 s : grpc.NewServer() //注册服务 pb.RegisterGreeterServer(s, server{}) // 在gRPC服务器上注册反射服务。 reflection.Register(s) if err : s.Serve(lis); err ! nil { log.Fatalf(failed to serve: %v, err) } }go语言实现GRPC远程调用 protobuf协议定义 创建一个 protobuf package,如 my_rpc_proto; 在$GOPATH/src/下创建 /my_grpc_proto/文件夹 里面创建 protobuf协议文件 helloServer.proto #到工作目录 $ CD $GOPATH/src/ #创建目录 $ mkdir grpc/myproto #进入目录 $ cd grpc/myproto #创建proto文件 $ vim helloServer.proto文件内容 syntax proto3;package myproto;//定义一个服务service HelloServer{//传入 传出rpc Sayhello(HelloReq) returns(HelloRsp){}rpc Sayname(NameReq) returns(NameRsp){} }//客户端发送给服务端 message HelloReq{string name 1; } //服务端发送给客户端 message HelloRsp{string msg 1; }message NameReq{string name 1; } message NameRsp{string msg 1; }在当前文件下编译 myproto.proto文件 $ protoc --go_out./ *.proto #不加grpc插件--这个无法生成SayHello等服务 $ protoc --go_outpluginsgrpc:./ *.proto #添加grpc插件 #对比发现内容增加 #得到 myproto.pb.go文件myproto.pb.go // Code generated by protoc-gen-go. DO NOT EDIT. // source: myproto.protopackage myprotoimport (context contextfmt fmtproto github.com/golang/protobuf/protogrpc google.golang.org/grpcmath math )// Reference imports to suppress errors if they are not otherwise used. var _ proto.Marshal var _ fmt.Errorf var _ math.Inf// This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ proto.ProtoPackageIsVersion2 // please upgrade the proto package// kehuduanfasonggeifuwuduan type HelloReq struct {Name string protobuf:bytes,1,opt,namename,proto3 json:name,omitemptyXXX_NoUnkeyedLiteral struct{} json:-XXX_unrecognized []byte json:-XXX_sizecache int32 json:- }func (m *HelloReq) Reset() { *m HelloReq{} } func (m *HelloReq) String() string { return proto.CompactTextString(m) } func (*HelloReq) ProtoMessage() {} func (*HelloReq) Descriptor() ([]byte, []int) {return fileDescriptor_04877df457807402, []int{0} }func (m *HelloReq) XXX_Unmarshal(b []byte) error {return xxx_messageInfo_HelloReq.Unmarshal(m, b) } func (m *HelloReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {return xxx_messageInfo_HelloReq.Marshal(b, m, deterministic) } func (m *HelloReq) XXX_Merge(src proto.Message) {xxx_messageInfo_HelloReq.Merge(m, src) } func (m *HelloReq) XXX_Size() int {return xxx_messageInfo_HelloReq.Size(m) } func (m *HelloReq) XXX_DiscardUnknown() {xxx_messageInfo_HelloReq.DiscardUnknown(m) }var xxx_messageInfo_HelloReq proto.InternalMessageInfofunc (m *HelloReq) GetName() string {if m ! nil {return m.Name}return }// fuwuduanfanhuigeikehuduan type HelloRsp struct {Msg string protobuf:bytes,1,opt,namemsg,proto3 json:msg,omitemptyXXX_NoUnkeyedLiteral struct{} json:-XXX_unrecognized []byte json:-XXX_sizecache int32 json:- }func (m *HelloRsp) Reset() { *m HelloRsp{} } func (m *HelloRsp) String() string { return proto.CompactTextString(m) } func (*HelloRsp) ProtoMessage() {} func (*HelloRsp) Descriptor() ([]byte, []int) {return fileDescriptor_04877df457807402, []int{1} }func (m *HelloRsp) XXX_Unmarshal(b []byte) error {return xxx_messageInfo_HelloRsp.Unmarshal(m, b) } func (m *HelloRsp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {return xxx_messageInfo_HelloRsp.Marshal(b, m, deterministic) } func (m *HelloRsp) XXX_Merge(src proto.Message) {xxx_messageInfo_HelloRsp.Merge(m, src) } func (m *HelloRsp) XXX_Size() int {return xxx_messageInfo_HelloRsp.Size(m) } func (m *HelloRsp) XXX_DiscardUnknown() {xxx_messageInfo_HelloRsp.DiscardUnknown(m) }var xxx_messageInfo_HelloRsp proto.InternalMessageInfofunc (m *HelloRsp) GetMsg() string {if m ! nil {return m.Msg}return }type NameReq struct {Name string protobuf:bytes,1,opt,namename,proto3 json:name,omitemptyXXX_NoUnkeyedLiteral struct{} json:-XXX_unrecognized []byte json:-XXX_sizecache int32 json:- }func (m *NameReq) Reset() { *m NameReq{} } func (m *NameReq) String() string { return proto.CompactTextString(m) } func (*NameReq) ProtoMessage() {} func (*NameReq) Descriptor() ([]byte, []int) {return fileDescriptor_04877df457807402, []int{2} }func (m *NameReq) XXX_Unmarshal(b []byte) error {return xxx_messageInfo_NameReq.Unmarshal(m, b) } func (m *NameReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {return xxx_messageInfo_NameReq.Marshal(b, m, deterministic) } func (m *NameReq) XXX_Merge(src proto.Message) {xxx_messageInfo_NameReq.Merge(m, src) } func (m *NameReq) XXX_Size() int {return xxx_messageInfo_NameReq.Size(m) } func (m *NameReq) XXX_DiscardUnknown() {xxx_messageInfo_NameReq.DiscardUnknown(m) }var xxx_messageInfo_NameReq proto.InternalMessageInfofunc (m *NameReq) GetName() string {if m ! nil {return m.Name}return }type NameRsp struct {Msg string protobuf:bytes,1,opt,namemsg,proto3 json:msg,omitemptyXXX_NoUnkeyedLiteral struct{} json:-XXX_unrecognized []byte json:-XXX_sizecache int32 json:- }func (m *NameRsp) Reset() { *m NameRsp{} } func (m *NameRsp) String() string { return proto.CompactTextString(m) } func (*NameRsp) ProtoMessage() {} func (*NameRsp) Descriptor() ([]byte, []int) {return fileDescriptor_04877df457807402, []int{3} }func (m *NameRsp) XXX_Unmarshal(b []byte) error {return xxx_messageInfo_NameRsp.Unmarshal(m, b) } func (m *NameRsp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {return xxx_messageInfo_NameRsp.Marshal(b, m, deterministic) } func (m *NameRsp) XXX_Merge(src proto.Message) {xxx_messageInfo_NameRsp.Merge(m, src) } func (m *NameRsp) XXX_Size() int {return xxx_messageInfo_NameRsp.Size(m) } func (m *NameRsp) XXX_DiscardUnknown() {xxx_messageInfo_NameRsp.DiscardUnknown(m) }var xxx_messageInfo_NameRsp proto.InternalMessageInfofunc (m *NameRsp) GetMsg() string {if m ! nil {return m.Msg}return }func init() {proto.RegisterType((*HelloReq)(nil), myproto.HelloReq)proto.RegisterType((*HelloRsp)(nil), myproto.HelloRsp)proto.RegisterType((*NameReq)(nil), myproto.NameReq)proto.RegisterType((*NameRsp)(nil), myproto.NameRsp) }func init() { proto.RegisterFile(myproto.proto, fileDescriptor_04877df457807402) }var fileDescriptor_04877df457807402 []byte{// 162 bytes of a gzipped FileDescriptorProto0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcd, 0xad, 0x2c, 0x28,0xca, 0x2f, 0xc9, 0xd7, 0x03, 0x93, 0x42, 0xec, 0x50, 0xae, 0x92, 0x1c, 0x17, 0x87, 0x47, 0x6a,0x4e, 0x4e, 0x7e, 0x50, 0x6a, 0xa1, 0x90, 0x10, 0x17, 0x4b, 0x5e, 0x62, 0x6e, 0xaa, 0x04, 0xa3,0x02, 0xa3, 0x06, 0x67, 0x10, 0x98, 0xad, 0x24, 0x03, 0x93, 0x2f, 0x2e, 0x10, 0x12, 0xe0, 0x62,0xce, 0x2d, 0x4e, 0x87, 0x4a, 0x83, 0x98, 0x4a, 0xb2, 0x5c, 0xec, 0x7e, 0x89, 0xb9, 0xa9, 0xb8,0x34, 0x4b, 0x43, 0xa5, 0xb1, 0xe9, 0x35, 0x2a, 0xe2, 0xe2, 0x06, 0x9b, 0x1c, 0x9c, 0x5a, 0x54,0x96, 0x5a, 0x24, 0x64, 0xc4, 0xc5, 0x11, 0x9c, 0x58, 0x99, 0x01, 0x12, 0x11, 0x12, 0xd4, 0x83,0xb9, 0x16, 0xe6, 0x36, 0x29, 0x74, 0xa1, 0xe2, 0x02, 0x25, 0x06, 0x21, 0x7d, 0x2e, 0xf6, 0xe0,0xc4, 0x4a, 0x90, 0x55, 0x42, 0x02, 0x70, 0x79, 0xa8, 0x83, 0xa4, 0xd0, 0x44, 0x40, 0x1a, 0x92,0xd8, 0xc0, 0x02, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xdc, 0xa8, 0x6b, 0x83, 0x0e, 0x01,0x00, 0x00, }// Reference imports to suppress errors if they are not otherwise used. var _ context.Context var _ grpc.ClientConn// This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. const _ grpc.SupportPackageIsVersion4// HelloServerClient is the client API for HelloServer service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type HelloServerClient interface {// 传入 Sayhello(ctx context.Context, in *HelloReq, opts ...grpc.CallOption) (*HelloRsp, error)Sayname(ctx context.Context, in *NameReq, opts ...grpc.CallOption) (*NameRsp, error) }type helloServerClient struct {cc *grpc.ClientConn }func NewHelloServerClient(cc *grpc.ClientConn) HelloServerClient {return helloServerClient{cc} }func (c *helloServerClient) Sayhello(ctx context.Context, in *HelloReq, opts ...grpc.CallOption) (*HelloRsp, error) {out : new(HelloRsp)err : c.cc.Invoke(ctx, /myproto.HelloServer/Sayhello, in, out, opts...)if err ! nil {return nil, err}return out, nil }func (c *helloServerClient) Sayname(ctx context.Context, in *NameReq, opts ...grpc.CallOption) (*NameRsp, error) {out : new(NameRsp)err : c.cc.Invoke(ctx, /myproto.HelloServer/Sayname, in, out, opts...)if err ! nil {return nil, err}return out, nil }// HelloServerServer is the server API for HelloServer service. type HelloServerServer interface {// chuanru chuanchuSayhello(context.Context, *HelloReq) (*HelloRsp, error)Sayname(context.Context, *NameReq) (*NameRsp, error) }func RegisterHelloServerServer(s *grpc.Server, srv HelloServerServer) {s.RegisterService(_HelloServer_serviceDesc, srv) }func _HelloServer_Sayhello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {in : new(HelloReq)if err : dec(in); err ! nil {return nil, err}if interceptor nil {return srv.(HelloServerServer).Sayhello(ctx, in)}info : grpc.UnaryServerInfo{Server: srv,FullMethod: /myproto.HelloServer/Sayhello,}handler : func(ctx context.Context, req interface{}) (interface{}, error) {return srv.(HelloServerServer).Sayhello(ctx, req.(*HelloReq))}return interceptor(ctx, in, info, handler) }func _HelloServer_Sayname_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {in : new(NameReq)if err : dec(in); err ! nil {return nil, err}if interceptor nil {return srv.(HelloServerServer).Sayname(ctx, in)}info : grpc.UnaryServerInfo{Server: srv,FullMethod: /myproto.HelloServer/Sayname,}handler : func(ctx context.Context, req interface{}) (interface{}, error) {return srv.(HelloServerServer).Sayname(ctx, req.(*NameReq))}return interceptor(ctx, in, info, handler) }var _HelloServer_serviceDesc grpc.ServiceDesc{ServiceName: myproto.HelloServer,HandlerType: (*HelloServerServer)(nil),Methods: []grpc.MethodDesc{{MethodName: Sayhello,Handler: _HelloServer_Sayhello_Handler,},{MethodName: Sayname,Handler: _HelloServer_Sayname_Handler,},},Streams: []grpc.StreamDesc{},Metadata: myproto.proto, }gRPC-Server编写 package main import ( net fmt google.golang.org/grpc pt demo/grpc/proto context ) const ( post 127.0.0.1:18881 ) //对象要和proto内定义的服务一样 type server struct{} //实现RPC SayHello 接口 func(this *server)SayHello(ctx context.Context,in *pt.HelloRequest)(*pt.HelloReplay , error){ return pt.HelloReplay{Message:helloin.Name},nil } //实现RPC GetHelloMsg 接口 func (this *server) GetHelloMsg(ctx context.Context, in *pt.HelloRequest) (*pt.HelloMessage, error) { return pt.HelloMessage{Msg: this is from server HAHA!}, nil } func main() { //监听网络 ln ,err :net.Listen(tcp,post) if err!nil { fmt.Println(网络异常,err) } // 创建一个grpc的句柄 srv: grpc.NewServer() //将server结构体注册到 grpc服务中 pt.RegisterHelloServerServer(srv,server{}) //监听grpc服务 err srv.Serve(ln) if err!nil { fmt.Println(网络启动异常,err) } }GRPC服务器简单案例 package mainimport (pd awesomeProject1/myprotocontextfmtgoogle.golang.org/grpcnet )type server struct{}func (this *server) Sayhello(ctx context.Context, req *pd.HelloReq) (*pd.HelloRsp, error) {return pd.HelloRsp{Msg: hello req.Name}, nil }func (this *server) Sayname(ctx context.Context, req *pd.NameReq) (*pd.NameRsp, error) {return pd.NameRsp{Msg: req.Name 早上好}, nil }func main() {//监听网络listen, err : net.Listen(tcp, :10086)if err ! nil {fmt.Println(网络错误, err)return}// 创建一个grpc的句柄--//创建网络srv : grpc.NewServer()//将server结构体注册到 grpc服务中--//注册服务pd.RegisterHelloServerServer(srv, server{})//监听grpc服务--//等待网络连接err srv.Serve(listen)if err ! nil {fmt.Println(网络错误, err)return}}gRPC-Client编写 package main import ( google.golang.org/grpc pt demo/grpc/proto fmt context ) const ( post 127.0.0.1:18881 ) func main() { // 客户端连接服务器 conn,err:grpc.Dial(post,grpc.WithInsecure()) if err!nil { fmt.Println(连接服务器失败,err) } defer conn.Close() //获得grpc句柄 c:pt.NewHelloServerClient(conn) // 远程调用 SayHello接口 //远程调用 SayHello接口 r1, err : c.SayHello(context.Background(), pt.HelloRequest{Name: panda}) if err ! nil { fmt.Println(cloud not get Hello server .., err) return } fmt.Println(HelloServer resp: , r1.Message) //远程调用 GetHelloMsg接口 r2, err : c.GetHelloMsg(context.Background(), pt.HelloRequest{Name: panda}) if err ! nil { fmt.Println(cloud not get hello msg .., err) return } fmt.Println(HelloServer resp: , r2.Msg) }GRPC客户端简单案例 package mainimport (pd awesomeProject1/myprotocontextfmtgoogle.golang.org/grpc )func main() {//客户端连接服务器 grpc.WithInsecure()---不安全 跳过服务器验证conn, err : grpc.Dial(127.0.0.1:10086, grpc.WithInsecure())if err ! nil {fmt.Println(网络异常, err)}//网络延迟关闭defer conn.Close()//获得grpc句柄client : pd.NewHelloServerClient(conn)通过句柄调用函数--context.Background()默认参数 这样就是传参sayhello, err : client.Sayhello(context.Background(), pd.HelloReq{Name: 熊猫})if err ! nil {fmt.Println(sayhello 服务调用失败)}fmt.Println(调用sayhello的返回, sayhello.Msg)Sayname, err : client.Sayname(context.Background(), pd.NameReq{Name: 托尼斯塔克})if err ! nil {fmt.Println(sayhello 服务调用失败)}fmt.Println(调用sayhello的返回, Sayname.Msg)}运行 #先运行 server后运行 client #如果反之则会报错四、Consul 为什么要学习consul服务发现 因为一套微服务架构中有很多个服务需要管理也就是说会有很多对grpc。 如果一一对应的进行管理会很繁琐所以我们需要有一个管理发现的机制 Consul的介绍 Consul是什么 Consul是HashiCorp公司推出的开源工具用于实现分布式系统的服务发现与配置。 Consul是分布式的、高可用的、可横向扩展的。它具备以下特性 : service discoveryconsul通过DNS或者HTTP接口使服务注册和服务发现变的很容易一些外部服务例如saas提供的也可以一样注册。 health checking健康检测使consul可以快速的告警在集群中的操作。和服务发现的集成可以防止服务转发到故障的服务上面。 key/value storage一个用来存储动态配置的系统。提供简单的HTTP接口可以在任何地方操作。 multi-datacenter无需复杂的配置即可支持任意数量的区域。 下面的例子有助于我们理解服务发现的形式 例如邮递员去某公司一栋大楼投递快件向门卫询问员工甲在哪一个房间门卫拿起桌上的通讯录查询告知邮递员员工甲在具体什么位置。假如公司来了一个员工乙他想让邮递员送过来就要先让门卫知道自己在哪一个房间需要去门卫那边登记员工乙登记后当邮递员向门卫询问时门卫就可以告诉邮递员员工乙的具体位置。门卫知道员工乙的具体位置的过程就是服务发现员工乙的位置信息可以被看作服务信息门卫的通讯录就是上文中提到的数据交换格式此例中员工乙就是上文的已方门卫就是服务发现的提供者。 什么是服务发现 微服务的框架体系中服务发现是不能不提的一个模块。我相信了解或者熟悉微服务的童鞋应该都知道它的重要性。这里我只是简单的提一下毕竟这不是我们的重点。我们看下面的一幅图片 图中客户端的一个接口需要调用服务A-N。客户端必须要知道所有服务的网络位置的以往的做法是配置是配 置文件中或者有些配置在数据库中。这里就带出几个问题 需要配置N个服务的网络位置加大配置的复杂性 服务的网络位置变化都需要改变每个调用者的配置 集群的情况下难以做负载反向代理的方式除外 总结起来一句话服务多了配置很麻烦问题多多 既然有这些问题那么服务发现就是解决这些问题的。话说怎么解决呢我们再看一张图 与之前一张不同的是加了个服务发现模块。图比较简单这边文字描述下。服务A-N把当前自己的网络位置注册到服务发现模块这里注册的意思就是告诉服务发现就以K-V的方式记录下K一般是服务名V就是IP:PORT。服务发现模块定时的轮询查看这些服务能不能访问的了这就是健康检查。客户端在调用服务A-N的时候就跑去服务发现模块问下它们的网络位置然后再调用它们的服务。这样的方式是不是就可以解决上面的问题了呢客户端完全不需要记录这些服务网络位置客户端和服务端完全解耦 Consul的安装 Consul用Golang实现因此具有天然可移植性 (支持 Linux、windows和macOS)。安装包仅包含一个可执行文 件。 Consul安装非常简单只需要下载对应系统的软件包并解压后就可使用。 下载安装 # 这里以 Linux系统为例 $ wget https://releases.hashicorp.com/consul/1.2.0/consul_1.2.0_linux_amd64.zip $ unzip consul_1.2.0_linux_amd64.zip $ mv consul /usr/local/bin/其它系统版本可在这里下载 https://www.consul.io/downloads.html 验证安装 安装 Consul后通过执行 consul命令你可以看到命令列表的输出 $ consulConsul 的角色 client: 客户端, 无状态, 将 HTTP 和 DNS 接口请求转发给局域网内的服务端集群. server: 服务端, 保存配置信息, 高可用集群, 在局域网内与本地客户端通讯, 通过广域网与其他数据中心通讯. 每个数据中心的 server 数量推荐为 3 个或是 5 个. 运行 Consul代理 Consul是典型的 C/S架构可以运行服务模式或客户模式。每一个数据中心必须有至少一个服务节点 3到5个服务节点最好。非常不建议只运行一个服务节点因为在节点失效的情况下数据有极大的丢失风险。 运行Agent 完成Consul的安装后,必须运行agent. agent可以运行为server或client模式.每个数据中心至少必须拥有一台server. 建议在一个集群中有3或者5个server.部署单一的server,在出现失败时会不可避免的造成数据丢失.其他的agent运行为client模式.一个client是一个非常轻量级的进程.用于注册服务,运行健康检查和转发对server的查询.agent必须在集群中的每个主机上运行. 启动 Consul Server #node1: $ consul agent -server -bootstrap-expect 2 -data-dir /tmp/consul -noden1 - bind192.168.110.123 -ui -config-dir /etc/consul.d -rejoin -join 192.168.110.123 - client 0.0.0.0 #运行cosnul agent以server模式 -server 定义agent运行在server模式 -bootstrap-expect 在一个datacenter中期望提供的server节点数目当该值提供的时候consul一直 等到达到指定sever数目的时候才会引导整个集群该标记不能和bootstrap共用 -data-dir提供一个目录用来存放agent的状态所有的agent允许都需要该目录该目录必须是稳定的系统 重启后都继续存在 -node节点在集群中的名称在一个集群中必须是唯一的默认是该节点的主机名 -bind该地址用来在集群内部的通讯集群内的所有节点到地址都必须是可达的默认是0.0.0.0 -ui 启动web界面 -config-dir配置文件目录里面所有以.json结尾的文件都会被加载 -rejoin使consul忽略先前的离开在再次启动后仍旧尝试加入集群中。 -clientconsul服务侦听地址这个地址提供HTTP、DNS、RPC等服务默认是127.0.0.1所以不对外提供服 务如果你要对外提供服务改成0.0.0.0#node2: $ consul agent -server -bootstrap-expect 2 -data-dir /tmp/consul -noden2 - bind192.168.110.148 -ui -rejoin -join 192.168.110.123 -server 定义agent运行在server模式 -bootstrap-expect 在一个datacenter中期望提供的server节点数目当该值提供的时候consul一直 等到达到指定sever数目的时候才会引导整个集群该标记不能和bootstrap共用 -bind该地址用来在集群内部的通讯集群内的所有节点到地址都必须是可达的默认是0.0.0.0 -node节点在集群中的名称在一个集群中必须是唯一的默认是该节点的主机名 -ui 启动web界面 -rejoin使consul忽略先前的离开在再次启动后仍旧尝试加入集群中。 -config-dir配置文件目录里面所有以.json结尾的文件都会被加载 -clientconsul服务侦听地址这个地址提供HTTP、DNS、RPC等服务默认是127.0.0.1所以不对外提供服 务如果你要对外提供服务改成0.0.0.0 -join 192.168.110.121 启动时加入这个集群启动 Consul Client #node3 $ consul agent -data-dir /tmp/consul -noden3 -bind192.168.110.124 -config-dir /etc/consul.d -rejoin -join 192.168.110.123 运行cosnul agent以client模式-join 加入到已有的集群中去。查看集群成员 新开一个终端窗口运行consul members, 你可以看到Consul集群的成员. $ consul members #节点 网络地址 状态 类型 版本 协议 数据中心 分管部分 Node Address Status Type Build Protocol DC Segment n1 192.168.110.7:8301 alive server 1.1.0 2 dc1 all n2 192.168.110.121:8301 alive server 1.1.0 2 dc1 all n3 192.168.110.122:8301 alive client 1.1.0 2 dc1 default停止Agent 你可以使用Ctrl-C 优雅的关闭Agent. 中断Agent之后你可以看到他离开了集群并关闭. 在退出中,Consul提醒其他集群成员,这个节点离开了.如果你强行杀掉进程.集群的其他成员应该能检测到这个节点失效了.当一个成员离开,他的服务和检测也会从目录中移除.当一个成员失效了,他的健康状况被简单的标记为危险,但是不会从目录中移除.Consul会自动尝试对失效的节点进行重连.允许他从某些网络条件下恢复过来.离开的节点则不会再继续联系. 此外,如果一个agent作为一个服务器,一个优雅的离开是很重要的,可以避免引起潜在的可用性故障影响达成一致性协议. consul优雅的退出 $ consul leave注册服务 搭建好conusl集群后用户或者程序就能到consul中去查询或者注册服务。可以通过提供服务定义文件或者调用HTTP API来注册一个服务. 首先,为Consul配置创建一个目录.Consul会载入配置文件夹里的所有配置文件.在Unix系统中通常类似/etc/consul.d (.d 后缀意思是这个路径包含了一组配置文件). $ mkdir /etc/consul.d然后,我们将编写服务定义配置文件.假设我们有一个名叫web的服务运行在 10000端口.另外,我们将给他设置一个标签.这样我们可以使用他作为额外的查询方式: {service: { #服务name: web, #名称tags: [master], #标记address: 127.0.0.1, #ipport: 10000, #端口checks: [{http: http://localhost:10000/health,interval: 10s #检查时间}]} }测试程序 package main import ( fmt net/http ) func handler(w http.ResponseWriter, r *http.Request) { fmt.Println(hello Web3! This is n3或者n2) fmt.Fprintf(w, Hello Web3! This is n3或者n2) } func healthHandler(w http.ResponseWriter, r *http.Request) { fmt.Println(health check! n3或者n2) } func main() { http.HandleFunc(/, handler) http.HandleFunc(/health, healthHandler) http.ListenAndServe(:10000, nil) }查询服务 一旦agent启动并且服务同步了.我们可以通过DNS或者HTTP的API来查询服务. DNS API 让我们首先使用DNS API来查询.在DNS API中,服务的DNS名字是 NAME.service.consul. 虽然是可配置的,但默认的所有DNS名字会都在consul命名空间下.这个子域告诉Consul,我们在查询服务,NAME则是服务的名称.对于我们上面注册的Web服务.它的域名是 web.service.consul : $ dig 127.0.0.1 -p 8600 web.service.consul有也可用使用 DNS API 来接收包含 地址和端口的 SRV记录: $ dig 127.0.0.1 -p 8600 web.service.consul SRVConsul架构 我们只看数据中心1可以看出consul的集群是由N个SERVER加上M个CLIENT组成的。而不管是SERVER还是CLIENT都是consul的一个节点所有的服务都可以注册到这些节点上正是通过这些节点实现服务注册信息的共享。除了这两个还有一些小细节一一简单介绍。 CLIENT CLIENT表示consul的client模式就是客户端模式。是consul节点的一种模式这种模式下所有注册到当前节点的服务会被转发到SERVER【通过HTTP和DNS接口请求server】本身是不持久化这些信息。SERVER SERVER表示consul的server模式表明这个consul是个server这种模式下功能和CLIENT都一样唯一不同的是它会把所有的信息持久化的本地这样遇到故障信息是可以被保留的 SERVER-LEADER 中间那个SERVER下面有LEADER的字眼表明这个SERVER是它们的老大它和其它SERVER不一样的一点是它需要负责同步注册的信息给其它的SERVER同时也要负责各个节点的健康监测。 Consul的client mode把请求转向server那么client的作用是什么 consul可以用来实现分布式系统的服务发现与配置。client把服务请求传递给serverserver负责提供服务以及和其他数据中心交互。题主的问题是既然server端提供了所有服务那为何还需要多此一举地用client端来接收一次服务请求。我想采用这种架构有以下几种理由 首先server端的网络连接资源有限。对于一个分布式系统一般情况下访问量是很大的。如果用户能不通过client直接地访问数据中心那么数据中心必然要为每个用户提供一个单独的连接资源(线程端口号等等)那么server端的负担会非常大。所以很有必要用大量的client端来分散用户的连接请求在client端先统一整合用户的服务请求然后一次性地通过一个单一的链接发送大量的请求给server端能够大量减少server端的网络负担。 其次在client端可以对用户的请求进行一些处理来提高服务的效率比如将相同的请求合并成同一个查询再比如将之前的查询通过cookie的形式缓存下来。但是这些功能都需要消耗不少的计算和存储资源。如果在server端提供这些功能必然加重server端的负担使得server端更加不稳定。而通过client端来进行这些服务就没有这些问题了因为client端不提供实际服务有很充足的计算资源来进行这些处理这些工作。 最后还有一点consul规定只要接入一个client就能将自己注册到一个服务网络当中。这种架构使得系统的可扩展性非常的强网络的拓扑变化可以特别的灵活。这也是依赖于client—server结构的。如果系统中只有几个数据中心存在那网络的扩张也无从谈起了。 Consul资料http://www.liangxiansen.cn/2017/04/06/consul https://blog.csdn.net/yuanyuanispeak/article/d etails/54880743
http://www.huolong8.cn/news/191453/

相关文章:

  • 重庆网站建设哪家公司那家好一级域名二级域名哪个好
  • 中英文网站源码 免费网站后台登录怎么做的
  • 惠州悦商做网站网站为什么要seo?
  • 定制网站建设功能报价表模板群晖wordpress主机
  • 网站建设管理职责好的网站首页的特点
  • 5118网站如何使用免费版中国建筑报道网
  • 三网合一的网站企业官方网站应该怎么样建设
  • 做网站需要交接什么网站建设分金手指排名十八
  • 新沂网站设计广州网站设计开发公司
  • 网站备案信息登记表免费的设计软件
  • 网站内外链怎么做效果好厦门市建设局官方网站
  • 网站版面做的很好的公司太原最新消息
  • 大地保险网站丰都网站建设哪家好
  • 上海松一网站建设设计中国北京2021
  • 济南软月建站做网站的做app的
  • 做网站资料创意设计活动加计扣除
  • 制作网站支付方式学设计的学校
  • 欧美做受网站视频播放厦门百度竞价开户
  • 深圳企业网站哪家强做PPT参考图片网站 知乎
  • 网站建设需要什么技能零基础家装设计师自学
  • 昆明做网站建设贵州做网站怎么推广
  • 化妆品网站主页设计关于网站及新媒体平台建设的规划
  • 提升访问境外网站速度百度网址大全 简单版
  • 做年会的网站wordpress 采集图片
  • 大型房产网站模板做二手衣服的网站有哪些
  • 怎么建设一个漫画网站个人做的网站有什么危险吗
  • jq动画效果网站有关网站空间正确的说法是
  • 安徽平台网站建设哪里好机关网站及新媒体建设实施方案
  • 中山企业网站制作百姓网二手车
  • 新网站建设特色郑州网站建设e00