SpringCloud结合Python,Golang实现多语言微服务架构

前言

SpringCloud微服务架构这几年已经使用非常广泛了,如下图是目前大多公司使用的基础业务架构图,通过围绕SpringCloud这个架构核心来做的各种部署和设计

在这里插入图片描述

但是随着业务量的增加,和部门团队的多样化,很多业务我们可能使用python或者golang实现从而更方便或者效率更高。比如国内某滴公司或者其他一些公司会使用到一些地图类的搜索算法,而这些算法大多使用python语言来做实现开源。

因此有上面这个例子引出我们这篇文章的思考,如何把python实现的微服务或者golang实现的微服务联合起我们已经的服务架构里,以实现更好的服务实现和平滑的访问切换,客户端无感知的状态

解决方案

Nginx反向代理实现

通过我们对基础架构图的了解来看实现结合多种语言来实现微服务的架构,比较简单的一种方案是通过nginx来配置反向代理实现,访问不同的语言的架构服务。设计图如下
在这里插入图片描述

这种设计的优点比较明显的就是不用考虑其他语言实现的微服务,直接通过nginx的配置,访问到对应的服务上;缺点是需要单独的配置不同的二级域名的访问,而且对于微服务的治理这块就是一块缺失的。因为这种方案的实现比较简单这里就不做过多介绍,虽然简单但是确实最可靠的一种方案。不会出现很多幺蛾子。

注册其他语言微服务到SpringCloud注册中心

第二种方案实现的架构图如下,也就是需要把其他语言实现的微服务加入到erueka的注册中心治理中,如果实现了这样的服务架构,那么对于客户端来说就是无感知的。对于服务端来说也是比较友好的。不同的团队或者个人负责不同的微服务的维护,同时兼容了个人掌握的技能,真正实现技能的充分发挥
在这里插入图片描述

但是实现这样架构的前提是,不同语言需要维护其对于注册中心的注册以及心跳的检测等逻辑,而且每个注册中心都有自己不同的协议,如果开发过程更换注册中心的话,这一项也是一个需要考虑的点。不过现在一般轮子各路大神都已经造好, 切换起来成本也不是很高。具体参考下面实现部分

特别注意的是这里探讨的技术实现目前还是处于实验阶段,虽然能建立如上图所示的基础架构,但是没有完全使用在生成环境中,生成环境最靠谱的做法还是第一种架构设计

Erueka客户端实现原理

erueka客户端的实现,通过java的源码可发现其主要是通过http协议和注册中心REST请求通讯从而维护了一套客户端注册,心跳,断开等场景处理
com.netflix.discovery.DiscoveryClientjava 核心实现在这里

  • 注册客户端信息
    POST /eureka/apps/{APP_NAME}
{
    "instance":{
        "instanceId":"192.168.1.107:golang-example:10000",
        "hostName":"192.168.1.107",
        "ipAddr":"192.168.1.107",
        "app":"golang-example",
        "port":{
            "@enabled":"true",
            "$":10000
        },
        "securePort":{
            "@enabled":"true",
            "$":443
        },
        "status":"UP",
        "overriddenStatus":"UNKNOWN",
        "dataCenterInfo":{
            "name":"MyOwn",
            "@class":"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo"
        }
    }
}
  • 心跳

心跳主要的目的就是维护注册中心持续的关注此客户端,否则一段时间注册中心收不到来自客户端的心跳请求的时候就会默认删除对此客户端的维护
PUT /eureka/apps/{APP_NAME}/{INSTANCE_ID}?status=UP&lastDirtyTimestamp={TIMESTAMP}

  • 获取已注册的服务列表
    GET /eureka/apps 或者 /eureka/apps/delta 前者获取全部的已注册的服务信息,后者则是获取增量的一些注册信息。
<application>
	<name>GOMOUDLE</name>
	<instance>
		<instanceId>192.168.0.22:gomoudle:8506</instanceId>
		<hostName>192.168.0.22</hostName>
		<app>GOMOUDLE</app>
		<ipAddr>192.168.0.22</ipAddr>
		<status>UP</status>
		<overriddenstatus>UNKNOWN</overriddenstatus>
		<port enabled="true">8506</port>
		<securePort enabled="false">7002</securePort>
		<countryId>1</countryId>
		<dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
			<name>MyOwn</name>
		</dataCenterInfo>
		<leaseInfo>
			<renewalIntervalInSecs>10</renewalIntervalInSecs>
			<durationInSecs>30</durationInSecs>
			<registrationTimestamp>1561625268208</registrationTimestamp>
			<lastRenewalTimestamp>1561687102598</lastRenewalTimestamp>
			<evictionTimestamp>0</evictionTimestamp>
			<serviceUpTimestamp>1561624932375</serviceUpTimestamp>
		</leaseInfo>
		<metadata>
			<SERVICE__VERSION__CODE>DEFAULT</SERVICE__VERSION__CODE>
			<VERSION>0.1.0</VERSION>
			<NODE__GROUP__ID>0</NODE__GROUP__ID>
			<PRODUCT__ENV__CODE>DEFAULT</PRODUCT__ENV__CODE>
			<PRODUCT__VERSION__CODE>DEFAULT</PRODUCT__VERSION__CODE>
			<PRODUCT__CODE>DEFAULT</PRODUCT__CODE>
		</metadata>
		<homePageUrl>http://192.168.0.22:8506</homePageUrl>
		<statusPageUrl>http://192.168.0.22:8506/info</statusPageUrl>
		<vipAddress>gomoudle</vipAddress>
		<secureVipAddress>gomoudle</secureVipAddress>
		<isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
		<lastUpdatedTimestamp>1561625268208</lastUpdatedTimestamp>
		<lastDirtyTimestamp>1561625268207</lastDirtyTimestamp>
		<actionType>ADDED</actionType>
	</instance>
</application>

当我们拿到如上这些已经注册到注册中心的客户端服务,就可以根据其ip进行http的数据通讯

  • 断开

当服务终止的时候需要从注册中心中移除,使用如下协议请求

DELETE /eureka/apps/{APP_NAME}/{INSTANCE_ID}

通过如上基础的于注册中心的通讯就能建立起一个eureka的客户端,由于其通讯协议是基于http的消息协议,也就为我们实现跨语言的微服务使用同一个注册中心打下了基础。需要注意的是在java里实现心跳连接和定时刷新服务列表是使用了heartbeatExecutorcacheRefreshExecutor 两个线程池来完成的。相应的我们在其他语言里也可以按照相同的设计原理来实现

实现

python微服务

python的一个比较轻量级的web服务框架tornado,可以快速搭建起基础的简易的http服务。
github.com/xuanbo/eureka-client 这里也有其他开发人员实现了eureka的python客户端,同时还支持跨服务的访问,基于这两个框架基础就很容实现python的微服务并注册到eureka注册中心,详细代码如下

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import string

import tornado.web
import py_eureka_client.eureka_client as eureka_client
from tornado.options import define, options

#当前服务监听的端口
define("port", default=8505, help="run on the given port", type=int)
#当前在eureka中注册的名称
define("appName", default='python', help="app name in eureka", type=string)


class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.write('[GET] python server...')

def eurekaclient():

    tornado.options.parse_command_line()
    #注册eureka服务
    eureka_client.init(eureka_server="http://127.0.0.1:8502/eureka/",
                                       app_name=options.appName,
                                       instance_port=options.port,
                                       # 调用其他服务时的高可用策略,可选,默认为随机
                                       ha_strategy=eureka_client.HA_STRATEGY_RANDOM,
                       data_center_name='MyOwn'
                       )
    app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()
    print("eureka exec")

if __name__ == "__main__":
    eurekaclient()

使用的代码比较简单,有兴趣的话我们自己可以也去实践一下实现对于服务注册到eureka的这个流程。这里我本地的注册中心端口是8502,网关端口是8504
http://localhost:8504/python/ 当访问网关加上我们的python应用名称能访问通的话,说明已经ok了。
注意需要把我们注册到注册中心的应用名称这里叫python加入到网关服务的配置中 zuul.routes.python=/python/\**

Go微服务

和python的流程类似,只是换了一种语言实现而已。具体流程和python的完全一致就不做特别的介绍。主要还是实现eureka客户端的这款是我们需要关注的核心,也是有待改造和完善的这里我们只做基础研究使用

package main

import (
	"fmt"
	"net/http"
	 eureka "github.com/xuanbo/eureka-client"
)

func main() {
	// create eureka client
	client := eureka.NewClient(&eureka.Config{
		DefaultZone:           "http://127.0.0.1:8502/eureka/",
		App:                   "gomoudle",
		Port:                  8506,
		RenewalIntervalInSecs: 10,
		DurationInSecs:        30,
		Metadata: map[string]interface{}{
			"VERSION":              "0.1.0",
			"NODE_GROUP_ID":        0,
			"PRODUCT_CODE":         "DEFAULT",
			"PRODUCT_VERSION_CODE": "DEFAULT",
			"PRODUCT_ENV_CODE":     "DEFAULT",
			"SERVICE_VERSION_CODE": "DEFAULT",
		},
	})
	// start client, register、heartbeat、refresh
	client.Start()

	http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
		// full applications from eureka server
		//apps := client.Applications
		//b, _ := json.Marshal(apps)
		name := "hello welcome go moudle"
		_, _ = writer.Write([]byte(name))
	})

	// start http server
	if err := http.ListenAndServe(":8506", nil); err != nil {
		fmt.Println(err)
	}
}

总结

谈起架构我们都知道没有一种架构是能适应所有的引用场景,只有根据我们自己的实际场景选择最适合我们的架构方案才是最优的。对于这里提到的两种架构方案,都有可取之处也有一定弊端。但是确都是可以通过我们不断的完善架构设计和代码设计来解决的。这里只是用eureka注册中心来举例,如果有兴趣可以尝试一下使用其他注册中心如何来实现跨语言的多微服务结合的使用原理大多是相通的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值