文章目录

  • 引言
    • 什么是微服务
      • 传统服务
      • 微服务
    • 什么是服务注册与服务发现
      • 为什么要使用不同的语言提供相同的服务能力
  • 服务协调器
  • 服务注册
    • Golang
    • C#(.NetCore3.1)
  • 服务发现
    • 通过HttpClient发现服务,并访问
    • 注销一个`coffee-service`实例再访问

引言

趁着最近休息写一篇关于微服务架构中特别重要一环服务注册与发现示例来互相探讨学习。

什么是微服务

传统服务

  • 举个栗子: 传统服务就类似于你们家附近的商店,这个商店可以提供你基本日常所需。你可以在里面买牙膏、零食、饮料、袜子、充电器等。
  • 优点
    1. 产品固定的情况下方便打理 (开发/维护效率高)
    2. 生意不错的情况下可按照当前模式快速在其他地方开分店 (易于部署)
  • 缺点
    1. 如果收银系统出问题就会导致商店无法正常营业 (宕机)
    2. 在店面商品已经放满的情况下添加新产品要么先存放在其他商品分类下 (耦合度高),要么只能扩大店面 (纵向扩展)

微服务

  • 举个栗子微服务就类似于一个商场,这个商场会有统一的入口,会有保安,导购台。商场里面会按照不同的商品类型开不同的“店”,例如卖“牙膏”、“牙刷”的一个店,卖“袜子”、“拖鞋”的一个店,卖“手机”、“充电器”的一个店。假设你想买一部手机,进入商场时你问了下门口的保安“哪个店可以买手机”,这时保安会说“出示一下健康码”(ApiGateway鉴权),绿码通过后,保安问了下身后的导购员 (服务发现),得到答案时保安就会告诉你哪一层哪个店卖手机。然后你就可以按照他的指引进去购买了。
  • 优点
    1. 当卖手机的店收银系统出现了问题,不影响其他店运营,也不会导致整个商场打烊 (服务隔离,分而治之)
    2. 如果哪天华为手机大卖,商场一个手机店不能承载用户消费了,可以在商场中再开一个手机店 (横向扩展)
  • 缺点
    1. 维护一个商场的治安、卫生较难 (可维护性较差)
    2. 商场发现小偷,寻找起来较麻烦 (线上问题修复时间长)

什么是服务注册与服务发现

服务注册与发现就类似于上面微服务例子中的导购员角色。她可以告诉访问者指定服务微服务系统中的哪个位置。

举个栗子:
最近放假,商场的咖啡店生意不错。于是我就拉着小明去商场开了两个咖啡店准备赚一笔。为什么开同时开两个呢,生意太好,如果小明那边客户比较多的话,就可以让部分客户到我这边来买 (负载均衡) 。还有如果哪天晚上我打游戏打晚了,早上起不来,小明就正常营业。或者是小明有事呢,我就正常营业 (熔断)
说干就干,两个咖啡店已经被我们如火如荼的置办起来了 (完成服务开发),我们给它起了个名字叫“三泡咖啡” (服务名称),小明的店在商场入口旁边门牌号是302,我的店在商场后面门牌号是609 (服务ID)
开业以后呢,每天早上,我和小明都会分别到导购台那边和导购小姐姐说“今天我们店正常营业”(不是撩小姐姐),这时导购小姐姐就会在小本本上记上我们的店和门牌号 (服务注册),之后进入商场的客人如果想买“三泡咖啡”,小姐姐就会按照她登记的信息告诉客人 (服务发现) 咖啡店在哪一层哪一号。
导购小姐姐呢也会定时来看我们店有没有存在突发情况,影不影响正常营业 (健康检查)。例如我这家店的收银系统今天出现问题了,导致无法正常营业了,那么导购小姐姐就会拿出小本本备注一下,下次再有客人想喝“三泡咖啡”,导购小姐姐就会将客人指向小明那家店了。

为什么要使用不同的语言提供相同的服务能力

本来我是想和小明分别购置一个自动咖啡机来为用户提供咖啡的,可是预算不足只能买一个。但没办法,我是老板,所以就给小明买了一个手磨咖啡机来做咖啡。不是说自动咖啡机一定比手磨咖啡机做的好,也不能说手磨咖啡机一定比自动咖啡机做出来的香。它们做出来的味道一样,只是结合了实际情况来定的。你说对吗? phper。

服务协调器

服务协调器就类似于上面例子中的导购员,常用的服务协调器有:ConsulEurekaZookeeperEtcd等。这个例子中我们就选用Consul来实现我们的服务注册与发现。

consulgoogle开源的一个使用go语言开发的服务发现、配置管理中心服务。内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案,不再需要依赖其他工具(比如ZooKeeper等)。服务部署简单,只有一个可运行的二进制的包。每个节点都需要运行agent,他有两种运行模式serverclient。每个数据中心官方建议需要3或5个server节点以保证数据安全,同时保证server-leader的选举能够正确的进行。

安装部署方式就请参考官方文档或百度一下吧。

官方地址:https://www.consul.io/

我这里是使用Docker部署的三个Consul实例

服务注册

Golang

使用Golang创建一个coffee-service服务,ID为coffee-service1

打开IDE在src目录下创建一个文件夹coffee,并添加coffeeServer.go文件,输入如下代码

package mainimport ("fmt""github.com/hashicorp/consul/api""net/http"
)func main()  {consulConfig := api.DefaultConfig()consulConfig.Address = "consul.insipid.top"               // consul 地址consulClient, err := api.NewClient(consulConfig)if err != nil {fmt.Println("new consul client err:", err)return}// 服务注册配置registerService := api.AgentServiceRegistration{ID:      "coffee-service1",                         // id唯一Name:    "coffee-service",                         // 服务名称,相同服务多实例注册下名称相同Tags:    []string{"demo"},                           // tagPort:    8082,                                        // 当前服务端口Address: "39.99.248.231",Check: &api.AgentServiceCheck{                      // 健康检查相关配置HTTP:      "http://39.99.248.231:8082/health",  // 健康检查接口,response code = 200表示检查通过Timeout:  "5s",                                   // 超时时间Interval: "5s",                                    // 检查间隔DeregisterCriticalServiceAfter: "10s",         // 检查失败后指定时间自动踢出无效服务},}// 注册当前配置服务到consulerr = consulClient.Agent().ServiceRegister(&registerService)if err!=nil{fmt.Println("注册到consul失败,err:",err)return}fmt.Println("注册到consul成功")// 添加健康检查接口,需要和上面注册服务配置信息中的健康检查path相同http.HandleFunc("/health", func(writer http.ResponseWriter, request *http.Request) {writer.Write([]byte("ok"))})// 业务处理http.HandleFunc("/get", func(writer http.ResponseWriter, request *http.Request) {writer.Write([]byte("欢迎光临三泡咖啡(609号店)"))})// 启动http服务器http.ListenAndServe(":8082",nil)
}

打开终端,在coffeeServer.go路径下,输入go mod init coffee,创建go mod文件,创建完成后再在终端输入go mod tidy拉取consul所需要的依赖包。拉取成功后如下:

module coffeego 1.17require github.com/hashicorp/consul/api v1.11.0require (github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirectgithub.com/fatih/color v1.9.0 // indirectgithub.com/hashicorp/go-cleanhttp v0.5.1 // indirectgithub.com/hashicorp/go-hclog v0.12.0 // indirectgithub.com/hashicorp/go-immutable-radix v1.0.0 // indirectgithub.com/hashicorp/go-rootcerts v1.0.2 // indirectgithub.com/hashicorp/golang-lru v0.5.0 // indirectgithub.com/hashicorp/serf v0.9.5 // indirectgithub.com/mattn/go-colorable v0.1.6 // indirectgithub.com/mattn/go-isatty v0.0.12 // indirectgithub.com/mitchellh/go-homedir v1.1.0 // indirectgithub.com/mitchellh/mapstructure v1.1.2 // indirectgolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae // indirect
)

在终端输入go run coffeeServer.go运行程序

在浏览器中输入localhost:8082/get访问,此时可以看到程序正常运行

打开Consul可视化面板consul.insipid.top发现,服务已经注册上去,但健康检查未通过,因为我的Consul是部署在云服务器上面的,访问不到本地电脑。所以现在需要将代码编译推送到云服务器上运行。

由于我的云服务器是centos系统,所以需要将GOOS设置为linux

设置完成后,编译coffeeServer.go并推送到云服务器

通过远程连接工具(XShell),连接到云服务器,并打开到推送的目录。设置执行权限并运行

程序运行成功后,再打开Consul可视化面板consul.insipid.top发现,服务已经注册并通过健康检查

C#(.NetCore3.1)

使用C#也创建一个coffee-service服务,功能与Golangcoffee-service一样,ID为coffee-service2

打开IDE新建一个空WebApi项目coffeeServer,添加Nuget包Consul

Startup.cs文件中输入如下代码:

using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;namespace coffeeServer
{public class Startup{public Startup(IConfiguration configuration){Configuration = configuration;}public IConfiguration Configuration { get; }public void ConfigureServices(IServiceCollection services){services.AddControllers();}public void Configure(IApplicationBuilder app, IWebHostEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}else{app.UseExceptionHandler("/Error");}app.UseRouting();app.UseEndpoints(endpoints =>{endpoints.MapControllerRoute("default", "{controller}/{action}");});var consulClient = new ConsulClient(x => { x.Address = new Uri("http://consul.insipid.top/"); });           // consul 地址var httpCheck = new AgentServiceCheck(){DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(10),                                              // 检查失败后指定时间自动踢出无效服务Interval = TimeSpan.FromSeconds(10),                                                                    // 检查间隔HTTP = "http://39.99.248.231:8083/Health",                                                              // 健康检查接口,response code = 200表示检查通过Timeout = TimeSpan.FromSeconds(5)                                                                       // 超时时间};var registration = new AgentServiceRegistration(){Checks = new[] { httpCheck },ID = "coffee-service2",                                                                                 // id唯一Name = "coffee-service",                                                                                // 服务名称,相同服务多实例注册下名称相同Address = "39.99.248.231",Port = 8083,Tags = new string[] { "demo" }                                                                          // tag};consulClient.Agent.ServiceRegister(registration).Wait();}}
}

新增一个控制器CoffeeController用于处理业务和健康检查(api接口和上面的golang项目保持一样)

using Microsoft.AspNetCore.Mvc;namespace coffeeServer.Controllers
{public class CoffeeController:ControllerBase{[HttpGet("get")]public IActionResult Get(){return Content("欢迎光临三泡咖啡(302号店)");}[HttpGet("health")]public IActionResult Health(){return Ok("ok");}}
}

本地运行成功,且服务已注册到Consul

此时可以看到coffee-service已经有两个示例了,红色为C#写的服务还没推送到云服务器,健康检查失败

编译推送C#程序到云服务器,由于.NetCore在Centos中需要安装.Net Runtime,安装步骤请自行百度

此时我们可以看见服务已成功运行

并在也注册到Consul,这时Consul的coffee-service已经有两个实例,一个通过Golang编写,一个通过C#编写

服务发现

当服务注册成功后,我们如果通过Consul来获取刚刚注册且健康的服务清单,其实就已经实现了负载均衡。 当然一般情况下我们会统一通过ApiGateway接入到Consul的方式来访问注册到Consul中的服务,但ApiGateway不是我们今天的主角,下次来讨论它。

通过HttpClient发现服务,并访问

打开刚刚的coffee文件夹下,添加coffeeClient.go文件,输入如下代码

package mainimport ("fmt""github.com/hashicorp/consul/api""io/ioutil""net/http""strconv"
)func main()  {consulConfig := api.DefaultConfig()consulConfig.Address="consul.insipid.top"                 // consul 地址registerClient, _ := api.NewClient(consulConfig)// 通过consul获取coffee-service的有效服务地址services, _, _ := registerClient.Health().Service("coffee-service", "demo", true, nil)for _,service := range services{getCoffeeUrl := "http://"+service.Service.Address+":"+strconv.Itoa(service.Service.Port)+"/get"fmt.Println("service:",getCoffeeUrl)response, err := http.Get(getCoffeeUrl)if err!=nil{fmt.Println("get err:",err)return}body, err:= ioutil.ReadAll(response.Body)if err!=nil{fmt.Println("read body err:",err)return}fmt.Println(string(body))response.Body.Close()}
}

打开终端执行go run coffeeClient.go,成功通过Consul获取到coffee-service的有效服务

注销一个coffee-service实例再访问

打开XShell,关闭coffee-service1实例

打开终端再次执行go run coffeeClient.go,发现刚刚通过Golang写的coffee-service1已经获取不到了

至此,我们已经完美实现不同语言(Golang&C#)提供相同服务能力给第三方调用了。

一般情况下,我们不会使用不同的技术栈来做相同服务的构建,都是看哪块业务哪个语言更适合。这个实验想表达的是,语言没有好坏,只要它支持跨平台方便移植那么它在互联网的技术海洋里总有一席之地的。

以上表述或步骤如有什么不妥,欢迎留言指正。谢谢~

一个故事,一段代码告诉你如何使用不同语言(GolangC#)提供相同的能力基于Consul做服务注册与发现相关推荐

  1. grpc双向流究竟是什么情况?2段代码告诉你

    本文分享自华为云社区<grpc双向流究竟是什么情况?2段代码告诉你>,作者:breakDawn. 为什么需要grpc双向流? 有时候请求调用和返回过程,并不是简单的一问一答形式,可能会涉及 ...

  2. 微服务之服务注册与发现--Eureka(附代码)

    微服务之服务注册与发现--Eureka(附代码) 该贴为入门贴,看完可快速知道服务注册与发现是什么?怎么用?至于深入的内容不在此篇文章所述之内,请自行百度. 内容来自:https://blog.csd ...

  3. 《一段代码告诉你,Python的正则表达式怎么用》

    嗨喽,小伙伴们大家早上好,中午好,晚上好呀, 又有好久没更新了,今天给大家分享一下python的正则表达式. 众所周知,python的正则表达式这类的工具类知识经常不用的话,容易忘记,所以我把它放在了 ...

  4. eureka多了一个莫名其妙的服务_SpringCloud 服务注册与发现组件 Eureka

    一.SpringCloud介绍 微服务,为了更好的创建项目组织结构.更高效的项目的迭代效果.更优良的架构设计,就需要使用微服务的架构思想,来对项目进行搭建或者重构. 企业碰到的第一个问题是服务如何进行 ...

  5. java字符下落,重力球,加速下落减速上弹,重力下落,这段代码是看到网上一个关...

    重力球,加速下落减速上弹,重力下落,这段代码是看到网上一个关 这段代码是看到网上一个关于碰壁球修改而成的,用到事件,画图,Timer类package Cheman;import javax.swing ...

  6. 低代码开发-牛刀低代码和专业开发云 JNPF 3.4.7新版源码 旗舰版 /微服务、单体 、JAVA 版本

    牛刀 Low-Code低代码开发云:开发效率和专业能力,缺一不可.牛刀Low-Code低代码开发云,高效全栈开发.跨端App开发,自由发布,灵活部署.提供开发.测试.部署.运维的一体化支持,真正低代码 ...

  7. 阿里面试败北:5种微服务注册中心如何选型?这几个维度告诉你!

    1.前言 微服务的注册中心目前主流的有以下五种: Zookeeper Eureka Consul Nacos Kubernetes 那么实际开发中到底如何选择呢?这是一个值得深入研究的事情,别着急,今 ...

  8. 一个故事告诉你,学习编程是否需要天赋?

    学习编程需要天赋吗? 一个故事告诉你,学习编程是否需要天赋? 1.你适合学习编程吗? 有的时候我也会怀疑我自己,我到底适不适合学习编程呢?感觉身边的同龄人都好牛X: 1.985/211 2.阿里/百度 ...

  9. 如何用代码讲述一段悲伤的故事?这些代码绝了!

    程序员的悲伤故事难道不应该是: 别人的老板晚上带他出去耍,你的老板半夜催你改代码: 别的程序员工资高.待遇好,而你只是血压高.心态好-- 擦干眼泪告诉自己:程序员前半生的悲伤都不是事儿,因为后半生你就 ...

最新文章

  1. boost::disjoint_sets_with_storage用法的测试程序
  2. 分享:session定义使用和丢失问题小结
  3. ISA Best Practices Analyzer Tool
  4. python制作图_Python做图的方法
  5. 收藏 | 12个神经网络可视化工具!
  6. 从C语言到C++的进阶之C++的非类新特性(篇二)
  7. 若依将ehcache改为redis缓存详细整合步骤
  8. Could not load file or assembly 'MagickNet.dll'
  9. ios8正式版固件开放下载 有哪些亮点?
  10. 智能网联V2X全球发展现状以及V2X平台设计简介
  11. 常见的图标库有哪些?
  12. Python代码打包在xp系统上运行~~
  13. Jmeter_基本操作-取样器
  14. (法)H.嘉当(H.Cartan)、塞尔(J.P.Serre)、施瓦茨(L.Schwartz)等[著],刘应明、胡师度[译]:代数结构与拓扑结构
  15. UML系列——包图Package
  16. 【蓝桥杯】——备战冲刺最后两周
  17. curl: (51) Unable to communicate securely with peer: requested domain name does not match the server
  18. CST设计接收贴片天线
  19. 中链云&神算云 全球发布暨表彰盛典在深顺利召开
  20. C语言学习笔记22/08/21

热门文章

  1. 计算机上自带的打字游戏,在学校上电脑课打字游戏的日子
  2. 手机风波(二)の惊喜篇
  3. 人工智能刷题(个人向)
  4. 化工厂在岗人员定位系统-化工厂人员定位-化工厂定位-新导智能
  5. 第一代程序员作家--王小波
  6. 如何给视频添加水印logo?
  7. 第1章 游戏之乐——连连看游戏设计
  8. 软件工程领域CCF B类会议:SANER介绍(以SANER 2019为例)
  9. MATLAB/ArcGIS读取nc数据并进行可视化
  10. Android系统自带的层次状态机StateMachine(Hierarchical State Machine)