有实力的网站建设推广/线上宣传渠道有哪些
文章目录
- 服务发现
- 集成consul
- 负载均衡
- 负载均衡算法
- 实现
- 配置中心
- nacos
服务发现
- 我们之前的架构是通过ip+port来调用的python的API,这样做的弊端是
- 如果新加一个服务,就要到某个服务改web(go)层的调用代码,配置IP/Port
- 并发过高需要加机器时,其他服务也要重新部署,否则找不到新加的node
- 如何解决这些问题呢?我们一般将多个服务进行注册
- 所有服务注册到注册中心,获取其他服务时通过这里拉取配置信息;(grpc也就是通过这配置的,传输序列化信息),其实就是将IP+Port放到这统一管理
- 注册中心必须有健康检查的功能,所以不能使用Redis
- 服务网关是对接用户的屏障,但也要能和注册中心交流,才能路由到具体服务
- 技术选型
- Paxos和Raft算法可以私下偷偷了解一下
- consul的安装和配置
-
能用docker就不搞太多麻烦的步骤
# 8500和8600端口常用 docker run -d -p 8500:8500 -p 8301:8301 -p 8302:8302 -p 8600:8600/udp consul consul agent -dev -client=0.0.0.0 docker container update --restart=always containerName/ID
-
访问consul:
ip:8500/ui/dc1/services
;可以看出,consul是支持KV存储的(但没有Redis强)
-
consul提供DNS功能(域名创建/解析),就是可以自定义域名;我们目前写的服务都可以通过ip+port方式注册和拉取,但如果有其他服务,不想通过consul,但希望能拉取各服务,这就需要consul能将自己和各服务域名化,方便外面的服务调用!
-
可以用
dig
命令查看consul的DNS功能,dig @ip -p 8600 cosul1.service.consul SRV
;就是在服务名后跟上service.consul,类似www.baidu.comyum install bind-utils
-
- consul的API接口
- 注册服务;使用PUT请求,路径也指定了,然后就是传递指定的参数了(json格式)
- 类似的,可以删除服务、配置健康检查
- 写个测试,先直接使用requests请求consul接口
import requestsheaders = {"contentType":"application/json" }def register(name, id, address, port):url = "http://192.168.109.129:8500/v1/agent/service/register"print(f"http://{address}:{port}/health")rsp = requests.put(url, headers=headers, json={"Name":name, # 服务名称 Specifies the logical name of the service."ID":id, # 服务ID Specifies a unique ID for this service"Tags":["mxshop", "Roy", "veritas", "web"],"Address":address, # 服务IP"Port":port, # 服务Port})if rsp.status_code == 200:print("注册成功")else:print(f"注册失败:{rsp.status_code}")# 启动service if __name__ == "__main__":register("mxshop-python", "mxshop-py", "192.168.109.1", 50051) # consul在Linux虚拟机(109.129),服务在Windows,用VMnet8的地址
- 删除服务
# 删除服务,有id即可 def deregister(id):url = f"http://192.168.109.129:8500/v1/agent/service/deregister/{id}"rsp = requests.put(url, headers=headers)if rsp.status_code == 200:print("注销成功")else:print(f"注销失败:{rsp.status_code}")
- 过滤服务;其实就是服务发现,输入你想找的服务名(这里还没有用DNS域名来搜索)
# 列出指定的服务 def filter_service(name):url = "http://192.168.109.129:8500/v1/agent/services"params = {"filter": f'Service == "{name}"'}rsp = requests.get(url, params=params).json()for key, value in rsp.items():print(key)
- 健康检查,先配置针对HTTP的,比较简单,grpc的会复杂一些,稍后整~
# 配置Check即可 def register(name, id, address, port):url = "http://192.168.109.129:8500/v1/agent/service/register"print(f"http://{address}:{port}/health")rsp = requests.put(url, headers=headers, json={"Name":name, # 服务名称 Specifies the logical name of the service."ID":id, # 服务ID Specifies a unique ID for this service"Tags":["mxshop", "Roy", "veritas", "web"],"Address":address, # 服务IP"Port":port, # 服务Port"Check": {# "GRPC":f"{address}:{port}",# "GRPCUseTLS": False,"HTTP":f"http://{address}:{port}/health", # 需要在go-web写这个路由的逻辑(handler,直接在main->initialize,不下到router->api了)"Timeout": "5s","Interval": "5s","DeregisterCriticalServiceAfter": "15s"}})if rsp.status_code == 200:print("注册成功")else:print(f"注册失败:{rsp.status_code}")
if __name__ == "__main__":# 注册go-webregister("mxshop-go", "mxshop-web", "192.168.109.1", 8021)# deregister("mshop-web")# 然后给它配上健康检查filter_service("mxshop-go") # 传name
// go-web package initializeimport ("github.com/gin-gonic/gin""mxshop/user-web/middlewares"router2 "mxshop/user-web/router""net/http" )func Routers() *gin.Engine {Router := gin.Default() // 全局 gin-context// 定义一个临时的路由,测试consul注册Router.GET("/health", func(context *gin.Context) {context.JSON(http.StatusOK, gin.H{"code": http.StatusOK,"success": true,})})Router.Use(middlewares.Cors()) // 配置跨域// 统一一下路由,传入group前缀,用户服务都用这个!ApiGroup := Router.Group("/u/v1") // 版本号,加个u,方便后续测试router2.InitUserRouter(ApiGroup)return Router } // 确保consul的容器能和服务ping通
- 配置GRPC的健康检查,假设我们要给python层的user-srv配置
- 参考官方文档,我们需要使用proto文件生成相关的代码;下面是生成好的代码,主要是实现proto文件中的
Check
和Watch
方法
- 当然是注册到当前的服务
server
,用到它这里的proto->pb2_grpc,和注册我们之前写的handler一样UserServicer
,这里要注册人家的handler:HealthServicer
- 代码补充:
# server.py # 注意这个路径,可能要改改 from common.grpc_health.v1 import health_pb2_grpc, health_pb2 from common.grpc_health.v1 import health# 注册健康检查 health_pb2_grpc.add_HealthServicer_to_server(health.HealthServicer(), server)
- consul中设置端口号,把HTTP换成这两行
"GRPC":f"{address}:{port}", "GRPCUseTLS": False, // 不检查证书啥的
- 要把服务启动,保证外部能ping通;测试一下
- 参考官方文档,我们需要使用proto文件生成相关的代码;下面是生成好的代码,主要是实现proto文件中的
- 注册服务;使用PUT请求,路径也指定了,然后就是传递指定的参数了(json格式)
- 上面是容器化的consul,搞个consul服务,也可以直接在python端,用第三方实现服务注册(还是consul)
- 安装:
pip install -i https://pypi.douban.com/simple python-consul2
;官方文档就在GitHub搜吧 - 这个东西对GRPC支持的不完善,就是比较省事;建议像上面,自己写服务注册的代码
import consulc = consul.Consul(host="192.168.109.129")address = "192.168.109.1" port = 50051 check={"GRPC":f"{address}:{port}","GRPCUseTLS": False,"Timeout": "5s","Interval": "5s","DeregisterCriticalServiceAfter": "15s" }# rsp = c.agent.service.register(name="user-srv", service_id="user-srv2", # address=address, port=port, tags=["mxshop"],check=check) rsp = c.agent.services() for key, val in rsp.items():rsp = c.agent.service.deregister(key) # print(rsp)
- 安装:
- 在go语言层面使用consul,类似在python写代码那样
- 包括服务注册(检查)和发现
package mainimport ("fmt""github.com/hashicorp/consul/api" )func Register(address string, port int, name string, tags []string, id string) error {cfg := api.DefaultConfig()cfg.Address = "192.168.1.103:8500"client, err := api.NewClient(cfg)if err != nil {panic(err)}//生成对应的检查对象,基于HTTP;直接实例化check := &api.AgentServiceCheck{HTTP: "http://192.168.109.129:8021/health",Timeout: "5s",Interval: "5s",DeregisterCriticalServiceAfter: "10s",}//生成注册对象,通过new方法生成对象;有啥区别呢?忘了registration := new(api.AgentServiceRegistration)registration.Name = nameregistration.ID = idregistration.Port = portregistration.Tags = tagsregistration.Address = addressregistration.Check = checkerr = client.Agent().ServiceRegister(registration)client.Agent().ServiceDeregister()if err != nil {panic(err)}return nil }func AllServices() {cfg := api.DefaultConfig()cfg.Address = "192.168.109.129:8500"client, err := api.NewClient(cfg)if err != nil {panic(err)}data, err := client.Agent().Services()if err != nil {panic(err)}for key, _ := range data {fmt.Println(key)} }// 服务发现 func FilterSerivice() {cfg := api.DefaultConfig()cfg.Address = "192.168.109.129:8500"client, err := api.NewClient(cfg)if err != nil {panic(err)}data, err := client.Agent().ServicesWithFilter(`Service == "user-web"`) // 写死了sorryif err != nil {panic(err)}for key, _ := range data {fmt.Println(key)} }func main() {//_ = Register("192.168.109.1", 8021, "user-web", []string{"mxshop", "Roy"}, "user-web")//AllServices()//FilterSerivice()fmt.Println(fmt.Sprintf(`Service == "%s"`, "user-srv")) }
- python层定义了逻辑提供给go层进行rpc,所以python层需要支持grpc的check,go层只需要支持HTTP的check
- python层也可以进行HTTP的check(只要是运行在ip+port上的服务),虽然不能通过HTTP直接调用(没路由)
- 问题:只定义了proto文件和handler注册,go怎么找到python API的?
grpc.Dial()
- 包括服务注册(检查)和发现
集成consul
- 上面是测试consul,各自进行了服务注册和模拟发现;现在需要集成到我们的项目,让python层和go-web都能用上
- 在python,服务注册;common/register,定义抽象类,并实现服务注册、删除、获取所有、发现
@abc.abstractmethod
- 之前引入了grpc的health-check,直接使用(for go-web);如果希望HTTP-DNS形式的调用呢?
- 在settings中加入consul服务器的配置(这个只能IP+Port,总得有个固定的手动操作的配置)
- 在server.py中调用,启动
from common.grpc_health.v1 import health_pb2_grpc, health_pb2 from common.grpc_health.v1 import health from common.register import consul from settings import settings# 注册健康检查 health_pb2_grpc.add_HealthServicer_to_server(health.HealthServicer(), server)# consul服务注册 logger.info(f"服务注册开始") register = consul.ConsulRegister(settings.CONSUL_HOST, settings.CONSUL_PORT) if not register.register(name=settings.SERVICE_NAME, id=settings.SERVICE_NAME,address=args.ip, port=args.port, tags=settings.SERVICE_TAGS, check=None):logger.info(f"服务注册失败")sys.exit(0) logger.info(f"服务注册成功")
- 在go,进行服务发现,但因为逻辑比较繁琐,放在initialize/srv_conn.go,然后做一个全局变量,放在global,这个脚本只需要赋值这个全局变量就好;记得在config加上配置(IP+Port)
package initializeimport ("fmt""github.com/hashicorp/consul/api"_ "github.com/mbobakov/grpc-consul-resolver" // It's important"go.uber.org/zap""google.golang.org/grpc""mxshop/user-web/global""mxshop/user-web/proto" )func InitSrvConn() {consulInfo := global.ServerConfig.ConsulInfouserConn, err := grpc.Dial(fmt.Sprintf("consul://%s:%d/%s?wait=14s", consulInfo.Host, consulInfo.Port, global.ServerConfig.UserSrvInfo.Name),grpc.WithInsecure(),grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`),)if err != nil {zap.S().Fatal("[InitSrvConn] 连接 【用户服务失败】")}userSrvClient := proto.NewUserClient(userConn)global.UserSrvClient = userSrvClient }func InitSrvConn2() {// 从注册中心获取到python层用户服务的信息cfg := api.DefaultConfig()consulInfo := global.ServerConfig.ConsulInfocfg.Address = fmt.Sprintf("%s:%d", consulInfo.Host, consulInfo.Port)userSrvHost := ""userSrvPort := 0client, err := api.NewClient(cfg)if err != nil {panic(err)}// 也可以使用转义符 \"%s\",用 " 代替 `data, err := client.Agent().ServicesWithFilter(fmt.Sprintf(`Service == "%s"`, global.ServerConfig.UserSrvInfo.Name))//data, err := client.Agent().ServicesWithFilter(fmt.Sprintf(`Service == "%s"`, global.ServerConfig.UserSrvInfo.Name))if err != nil {panic(err)}for _, value := range data {userSrvHost = value.AddressuserSrvPort = value.Portbreak}if userSrvHost == "" {zap.S().Fatal("[InitSrvConn] 连接 【用户服务失败】")return}// 拨号连接用户grpc服务器 跨域的问题 - 后端解决 也可以前端来解决userConn, err := grpc.Dial(fmt.Sprintf("%s:%d", userSrvHost, userSrvPort), grpc.WithInsecure())if err != nil {zap.S().Errorw("[GetUserList] 连接 【用户服务失败】","msg", err.Error(),)}//1. 后续的用户服务下线了 2. 改端口了 3. 改ip了 怎么办? 负载均衡来做//2. 已经事先创立好了连接,这样后续就不用进行再次tcp的三次握手//3. 一个连接多个groutine共用,要性能提升 - 可以使用连接池;不是很理解,一个用户难道能同时多个操作?userSrvClient := proto.NewUserClient(userConn)global.UserSrvClient = userSrvClient } // 记得在main中调用
- grpc.Dial()也放到这,得到python层的服务信息(IP+Port)后直接建立连接
- 两条主要流程贯穿:main-init-config-yml (viper支持);main-init-router-api/handler(global)
- 为了方便测试可以先注释掉验证码,我们是基于内存做的,所以只是设置false不管用,除非改成基于Redis的
负载均衡
- 先解决一个问题,动态获取随机可用端口号
def get_free_tcp_port():tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)tcp.bind(("", 0))_, port = tcp.getsockname()tcp.close()return port
- 这样我们就可以在service运行的时候不必指定端口号
# 还是在server.py改 def serve():parser = argparse.ArgumentParser()parser.add_argument('--ip',nargs="?",type=str,default="192.168.0.103",help="binding ip")parser.add_argument('--port',nargs="?",type=int,default=0,help="the listening port")args = parser.parse_args()if args.port == 0:port = get_free_tcp_port()else:port = args.port # 服务注册的时候也要改成port
- 类似的,go-web那边也要改,我们放在utils下
package utilsimport ("net" )func GetFreePort() (int, error) {addr, err := net.ResolveTCPAddr("tcp", "localhost:0")if err != nil {return 0, err}l, err := net.ListenTCP("tcp", addr)if err != nil {return 0, err}defer l.Close()return l.Addr().(*net.TCPAddr).Port, nil }
- go需要python的服务,port动态了,就必须靠consul的服务发现,任何层的任何服务都注册到consul,通过服务名称即可发现
- 什么是负载均衡?
- 这个问题说起来挺复杂的,比如我们可以将架构做成如图所示
- 因为一个网关也不能扛得住还得是集群;通过NGINX做负载均衡如今是业界公认,可以参考我的笔记
- 这个问题说起来挺复杂的,比如我们可以将架构做成如图所示
- 还有一种方法就是进程内的load balance,将负载均衡的算法以SDK的方式实现在客户端进程内
- 最大的区别就是没有负载均衡器,比如上图的NGINX,这样更稳一点
- 如图,在用户-web(consumer)实现,就事先让web的机器使用协程将所有的能提供用户-srv的机器连上,然后用特定的算法在本地(提供web服务的这个机器自身)安排如何使用服务
- 不太好的就是不同的语言要写不同的SDK(因为依赖web进程的协程),但grpc还是用这种主流方法
- 还有一种改进的方法就是,将load balance算法和服务发现(连接服务)在web机器上以单独的进程实现
- 这个方法避免了写不同的语言的SDK;当然也有缺陷,增加维护成本,还要单独监控
- 这个方法避免了写不同的语言的SDK;当然也有缺陷,增加维护成本,还要单独监控
负载均衡算法
- 最简单的就是轮循法,挨着拿活;还有一致性hash,可以了解
实现
- 还是借助grpc实现负载均衡,根据官方文档的描述,它提供了综合策略
- 均衡算法可以是第三方,也可以自己搞(最好不要使用第三方的服务)
- 本身也没有继承服务注册中心,但是留了接口,可以指定从哪进行服务发现;而且有人写了包用于集成grpc和consul实现了负载均衡,测试一下,关键是把consul的URL配置写正确
- 当然,用到grpc必须把那份proto拿进来,这次拨号是拨consul服务的
- 测试代码,python层启动两个server,注意这里需要
import uuid
得到service_id,不然consul还是只有一个user_srv;并使用partial()
将on_exit()
包装成其他函数package mainimport ("context""fmt""log""mxshop/user-web/grpclb_test/proto"// 导入却没有使用,其实底层是在import时,在init中注册到了grpc_ "github.com/mbobakov/grpc-consul-resolver" // It's important"google.golang.org/grpc" )func main() {conn, err := grpc.Dial(// 这里的名字要跟consul中注册的一样,tag也必须一样"consul://192.168.109.129:8500/user-srv?wait=14s&tag=srv",grpc.WithInsecure(),grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`), // 轮询)if err != nil {log.Fatal(err)}defer conn.Close()// 为了在一次进程中连续请求,查看负载均衡的效果;如果采用多次启动,每次轮循的起点都是0,没效果for i := 0; i < 10; i++ {userSrvClient := proto.NewUserClient(conn)rsp, err := userSrvClient.GetUserList(context.Background(), &proto.PageInfo{Pn: 1,PSize: 2,})if err != nil {panic(err)}for index, data := range rsp.Data {fmt.Println(index, data)}}}
- 再温习一下grpc,这里单独测试使用的是Dial(cfg)+proto.NewUserClient();项目里集成使用api.NewUserClient()+cfg的形式(包含了拨号)
- 集成到项目,更新InitSrvConn(),使用测试形式(手动拨号)
func InitSrvConn() {consulInfo := global.ServerConfig.ConsulInfouserConn, err := grpc.Dial(fmt.Sprintf("consul://%s:%d/%s?wait=14s", consulInfo.Host, consulInfo.Port, global.ServerConfig.UserSrvInfo.Name),grpc.WithInsecure(),grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`),)if err != nil {zap.S().Fatal("[InitSrvConn] 连接 【用户服务失败】")}userSrvClient := proto.NewUserClient(userConn)global.UserSrvClient = userSrvClient }
配置中心
- 目前我们的配置是基于本地的配置文件,如果配置文件修改了,需要重启服务,这很不合适,所以引入了viper,可以自动监听配置文件的修改,但还是会有一些问题
- 总结起来就是:不改还行,改了必死,肯定会改,必死!所以我们使用配置中心,类似服务注册中心
- 支持的功能:
- 实例可以拉取配置
- 权限控制
- 配置回滚
- 环境隔离
- 搭建集群
- 技术选型,流行的框架都是Java支持的,但是也有多语言的,比如Apollo或者nacos
nacos
- 学会一个其他就会了,nacos是阿里做的
- 安装还是使用docker,如果懂点Java也可以手动安装,执行命令
docker run --name nacos-standalone -e MODE=standalone -e JVM_XMS=512m -e JVM_XMX=512m -e JVM_XMN=256m -p 8848:8848 -d nacos/nacos-server:latest # 访问:192.168.109.129:8848/nacos,nacos/nacos # 注意上面那个mode要大写,不然可能
- 登录之后尝试使用,有一些概念
- 命名空间:可以隔离配置集,一般一个微服务一个命名空间
- 组:就是命名空间内,区分测试配置和生产环境配置的
- dataid:一个配置集,其实就是一个配置文件
- 集成到python层
-
直接安装:
pip install nacos-sdk-python
,因为同步到pypi了 -
把settings中的字段加上去;使用json很方便,所以我们在nacos尽量使用json;user-srv.json,分group dev/pro
{"name": "user-srv","tags": ["Roy", "mxshop", "python"],"mysql": {"db": "mxshop_user_srv","host": "192.168.109.129","port": 3306,"user": "root","psd": "root"},"consul": {"host": "192.168.109.129","port": 8500} }
-
settings就根据namespace+group+dataid定位并拉取配置,用data赋值
import jsonimport nacos from playhouse.pool import PooledMySQLDatabase from playhouse.shortcuts import ReconnectMixin from loguru import logger# 使用peewee的连接池, 使用ReconnectMixin来防止出现连接断开查询失败 class ReconnectMysqlDatabase(ReconnectMixin, PooledMySQLDatabase):passNACOS = {"Host": "192.168.109.129","Port": 8848,"NameSpace": "c1872978-d51c-4188-a497-4e0cd20b97d5", # 新建user namespace"User": "nacos","Password": "nacos","DataId": "user-srv.json","Group": "dev" }# 这个client就是NacosClient的实例,包含了我们需要的方法 client = nacos.NacosClient(f'{NACOS["Host"]}:{NACOS["Port"]}', namespace=NACOS["NameSpace"],username=NACOS["User"],password=NACOS["Password"])# get config data = client.get_config(NACOS["DataId"], NACOS["Group"]) data = json.loads(data) logger.info(data)# 这里 def update_cfg(args):print("配置产生变化")print(args)# consul的配置 CONSUL_HOST = data["consul"]["host"] CONSUL_PORT = data["consul"]["port"]# 服务相关的配置 SERVICE_NAME = data["name"] SERVICE_TAGS = data["tags"]DB = ReconnectMysqlDatabase(data["mysql"]["db"], host=data["mysql"]["host"], port=data["mysql"]["port"],user=data["mysql"]["user"], password=data["mysql"]["password"])
-
这里的
update_cfg()
函数是添加watcher时使用的,如果监听到config变化,会执行这个函数;官网有介绍:add_config_watchers(data_id, group, cb_list)
-
这里不能直接在settings添加,会报错:
# serve.py if __name__ == '__main__':logging.basicConfig()settings.client.add_config_watcher(settings.NACOS["DataId"], settings.NACOS["Group"], settings.update_cfg)serve()
-
- 集成到go-web层(先去nacos发布,这里搞成yaml也行哈),基本上跟着官网的代码就很可以
- go本身支持json(json-tag),所以我们就把yaml转换一下;
- 之前的配置字段都放在config/config.go(而且变成了struct方便操作);现在拉取json字符串后想要解析成对应的struct,当然要在struct中加上json-tag(之前的mapstructure:"name"是针对viper的)
- 然后定义NacosConfig的struct,viper只需要用本地配置文件实例化它就可以
type NacosConfig struct {Host string `mapstructure:"host"`Port uint64 `mapstructure:"port"`Namespace string `mapstructure:"namespace"`User string `mapstructure:"user"`Password string `mapstructure:"password"`DataId string `mapstructure:"dataid"`Group string `mapstructure:"group"` }
// config-debug.yaml 本地配置文件 host: '192.168.0.104' port: 8848 namespace: 'c1872978-d51c-4188-a497-4e0cd20b97d5' user: 'nacos' password: 'nacos' dataid: 'user-web.json' group: 'dev'
- 更新init/config.go;前面的ReadConfig目的是获取Nacos的连接信息,获取配置content
import ("github.com/nacos-group/nacos-sdk-go/clients""github.com/nacos-group/nacos-sdk-go/common/constant""github.com/nacos-group/nacos-sdk-go/vo" )func InitConfig() {debug := GetEnvInfo("IS_DEBUG")configFilePrefix := "config"configFileName := fmt.Sprintf("user-web/%s-pro.yaml", configFilePrefix)if debug {configFileName = fmt.Sprintf("user-web/%s-debug.yaml", configFilePrefix)}v := viper.New()// 文件的路径如何设置v.SetConfigFile(configFileName)if err := v.ReadInConfig(); err != nil {panic(err)}// 这个对象如何在其他文件中使用 - 全局变量if err := v.Unmarshal(global.NacosConfig); err != nil { // 解析nacos的连接信息,赋值给NacosConfigpanic(err)}zap.S().Infof("配置信息存放在:: &v", global.NacosConfig)// 从nacos中读取配置信息sc := []constant.ServerConfig{{IpAddr: global.NacosConfig.Host,Port: global.NacosConfig.Port,},}cc := constant.ClientConfig{NamespaceId: global.NacosConfig.Namespace, // 如果需要支持多namespace,我们可以场景多个client,它们有不同的NamespaceIdTimeoutMs: 5000,NotLoadCacheAtStart: true,LogDir: "tmp/nacos/log",CacheDir: "tmp/nacos/cache",RotateTime: "1h",MaxAge: 3,LogLevel: "debug",}configClient, err := clients.CreateConfigClient(map[string]interface{}{"serverConfigs": sc,"clientConfig": cc,})if err != nil {panic(err)}content, err := configClient.GetConfig(vo.ConfigParam{DataId: global.NacosConfig.DataId,Group: global.NacosConfig.Group})if err != nil {panic(err)}//fmt.Println(content) //字符串 - yaml// 想要将一个json字符串转换成struct,需要到struct设置json-tag// 再用拉取到的content,赋值项目的配置信息err = json.Unmarshal([]byte(content), &global.ServerConfig)if err != nil {zap.S().Fatalf("读取nacos配置失败: %s", err.Error())}fmt.Println(&global.ServerConfig) }
- 记得在mx-shop下新建tmp/nacos/log和tmp/nacos/cache目录,存放缓存,也便于回滚
- 接下来就要继续完善其他服务了
- 先开始Java-Web
相关文章:

Python+Go实践(电商架构三)
文章目录服务发现集成consul负载均衡负载均衡算法实现配置中心nacos服务发现 我们之前的架构是通过ipport来调用的python的API,这样做的弊端是 如果新加一个服务,就要到某个服务改web(go)层的调用代码,配置IP/Port并发…...

基于 MySQL 排它锁实现分布式可重入锁解决方案
一、MySQL 排它锁和共享锁 在进行实验前,先来了解下MySQL 的排它锁和共享锁,在 MySQL 中的锁分为表锁和行锁,在行锁中锁又分成了排它锁和共享锁两种类型。 1. 排它锁 排他锁又称为写锁,简称X锁,是一种悲观锁&#x…...

【大数据】Hadoop-HA-Federation-3.3.1集群高可用联邦安装部署文档(建议收藏哦)
背景概述 单 NameNode 的架构使得 HDFS 在集群扩展性和性能上都有潜在的问题,当集群大到一定程度后,NameNode 进程使用的内存可能会达到上百 G,NameNode 成为了性能的瓶颈。因而提出了 namenode 水平扩展方案-- Federation。 Federation 中…...

【设计模式之美 设计原则与思想:面向对象】14 | 实战二(下):如何利用面向对象设计和编程开发接口鉴权功能?
在上一节课中,针对接口鉴权功能的开发,我们讲了如何进行面向对象分析(OOA),也就是需求分析。实际上,需求定义清楚之后,这个问题就已经解决了一大半,这也是为什么我花了那么多篇幅来讲…...

工作技术小结
2023/1/31 关于后端接口编写小结 1,了解小程序原型图流程和细节性的东西 2,数据库关联结构仔细分析,找到最容易查询的关键字段,标语表之间靠什么关联 2023/2/10 在web抓包过程中,如果要实现批量抓取,必须解…...

无重复字符的最长子串-力扣3-java
一、题目描述给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。示例 1:输入: s "abcabcbb"输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。示例 2:输入: s "bbbbb"输出: 1解释: 因为…...

java ssm高校教材管理平台 idea maven
设计并且实现一个基于JSP技术的高校教材管理平台的设计与实现。采用MYSQL为数据库开发平台,SSM框架,Tomcat网络信息服务作为应用服务器。高校教材管理平台的设计与实现的功能已基本实现,主要学生、教材管理、学习教材、教材入库、教材领取、缴…...

【Python学习笔记】25.Python3 输入和输出(1)
前言 在前面几个章节中,我们其实已经接触了 Python 的输入输出的功能。本章节我们将具体介绍 Python 的输入输出。 输出格式美化 Python两种输出值的方式: 表达式语句和 print() 函数。 第三种方式是使用文件对象的 write() 方法,标准输出文件可以用…...

C++复习笔记8
泛型编程:编写的是与类型无关的通用代码,是代码复用的一种手段,模板是泛型编程的基础。 1.函数模板:类型参数化,增加代码复用性。例如对于swap函数,不同类型之间进行交换都需要进行重载,但是函数…...

RabbitMQ入门
目录1. 搭建示例工程1.1. 创建工程1.2. 添加依赖2. 编写生产者3. 编写消费者4. 小结需求 官网: https://www.rabbitmq.com/ 需求:使用简单模式完成消息传递 步骤: ① 创建工程(生成者、消费者) ② 分别添加依赖 ③ 编…...

【计算机网络】Linux环境中的TCP网络编程
文章目录前言一、TCP Socket API1. socket2. bind3. listen4. accept5. connect二、封装TCPSocket三、服务端的实现1. 封装TCP通用服务器2. 封装任务对象3. 实现转换功能的服务器四、客户端的实现1. 封装TCP通用客户端2. 实现转换功能的客户端五、结果演示六、多进程版服务器七…...

idekCTF 2022 比赛复现
Readme 首先 []byte 是 go 语言里面的一个索引,比如: package mainimport "fmt"func main() {var str string "hello"var randomData []byte []byte(str)fmt.Println(randomData[0:]) //[104 101 108 108 111] }上面这串代码会从…...

jvm的类加载过程
加载 通过一个类的全限定名获取定义此类的二进制字节流将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口链接 验证 验证内容的合法性准备 把方法区的静态变量初…...

VOC数据增强与调整大小
数据增强是针对数据集图像数量太少所采取的一种方法。 博主在实验过程中,使用自己的数据集时发现其数据量过少,只有280张,因此便想到使用数据增强的方式来获取更多的图像信息。对于图像数据,我们可以采用旋转等操作来获取更多的图…...

Linux 安装jenkins和jdk11
Linux 安装jenkins和jdk111. Install Jdk112. Jenkins Install2.1 Install Jenkins2.2 Start2.3 Error3.Awakening1.1 Big Data -- Postgres4. Awakening1. Install Jdk11 安装jdk11 sudo yum install fontconfig java-11-openjdk 2. Jenkins Install 2.1 Install Jenkins 下…...

Pandas——Series操作【建议收藏】
pandas——Series操作 作者:AOAIYI 创作不易,觉得文章不错或能帮助到你学习,可以点赞收藏评论哦 文章目录pandas——Series操作一、实验目的二、实验原理三、实验环境四、实验内容五、实验步骤1.创建Series2.从具体位置的Series中访问数据3.使…...

JUC并发编程Ⅰ -- Java中的线程
文章目录线程与进程并行与并发进程与线程应用应用之异步调用应用之提高效率线程的创建方法一:通过继承Thread类创建方法二:使用Runnable配合Thread方法三:使用FutureTask与Thread结合创建查看进程和线程的方法线程运行的原理栈与栈帧线程上下…...

基于vue-admin-element开发后台管理系统【技术点整理】
一、Vue点击跳转外部链接 点击重新打开一个页面窗口,不覆盖当前的页面 window.open(https://www.baidu.com,"_blank")"_blank" 新打开一个窗口"_self" 覆盖当前的窗口例如:导入用户模板下载 templateDownload() {wi…...

【C语言学习笔记】:通讯录管理系统
系统中需要实现的功能如下: ✿ 添加联系人:向通讯录中添加新人,信息包括(姓名、性别、年龄、联系电话、家庭住址)最多记录1000人 ✿ 显示联系人:显示通讯录中所有的联系人信息 ✿ 删除联系人:按…...

开关电源环路稳定性分析(10)——OPA和OTA型补偿器传递函数
大家好,这里是大话硬件。 在前面9讲的内容中将开关电源环路分析进行了梳理,我相信很多人即使都看完了,应该还是不会设计,而且还存在几个疑问。比如我随便举几个: 开关电源的带宽怎么设定?开关电源精度和什…...

2.11知识点整理(关于pycharm,python,pytorch,conda)
pycharm 设置anaconda环境: File -> Settings->选择左侧的project xxx再选择打开Project Interpreter页->选择add添加解释器->添加Anaconda中Python解释器(Anaconda安装目录下的python.exe) (选择existing environment ÿ…...

Linux服务器开发-2. Linux多进程开发
文章目录1. 进程概述1.1 程序概览1.2 进程概念1.3 单道、多道程序设计1.4 时间片1.5 并行与并发1.6 进程控制块(PCB)2. 进程的状态转换2.1 进程的状态2.2 进程相关命令查看进程实时显示进程动态杀死进程进程号和相关函数3. 进程的创建-fork函数3.1 进程创…...

Excel中缺失数据值的自动填充
目录简单方法示例1:数据满足线性趋势示例2:数据满足增长(指数)趋势参考实验做完处理数据,发现有一组数据因为设备中途出现问题缺失了,之前做过的数据也找不到,为了不影响后续处理,这里使用Excel插入缺失值。…...

路由器刷固件
前言 我希望可以远程访问我的电脑。但,我不希望电脑总是处于运行状态,因为那样比较费电。所以需要一个方案,能将睡眠/关机中的电脑唤醒。 方案一:选用智能插座,远程给电脑上电。电脑设置上电自启。但,这存…...

leetcode: Two Sum II - Input Array is Sorted
leetcode: Two Sum II - Input Array is Sorted1. 题目2. 解答3. 总结1. 题目 Given a 1-indexed array of integers numbers that is already sorted in non-decreasing order, find two numbers such that they add up to a specific target number. Let these two number…...

STL——list
一、list介绍及使用 1. list文档介绍 (1)list是可以在常数范围内,在任意位置进行插入、删除的序列式容器,并且该容器可以前后双向迭代。 (2)list的底层是带头结点的双向循环链表,其中每个元素…...

实战打靶集锦-004-My-Cmsms
**写在前面:**记录一次艰难曲折的打靶经历。 目录1. 主机发现2. 端口扫描3. 服务枚举4. 服务探查4.1 WEB服务探查4.1.1 浏览器访问4.1.2 目录枚举4.1.3 控制台探查4.1.4 其他目录探查4.2 阶段小结5. 公共EXP搜索5.1 CMS搜索5.2 Apache搜索5.3 PHP搜索5.4 MySQL搜索5…...

c++代码实现我的世界(14)
c代码实现我的世界14|生成地貌兼工作台1前言的前言~前言生成地貌函数结构体struct dimao根据比例生成地貌工作台函数准备的东西写在最后前言的前言~ 实在对不起大家,有挺长时间没更新了。 前言 今天我们将写生成地形的函数与工作台前传的代码; 注&…...

RMQ--区间最值问题(在更)
RMQ(Range Minimum/Maximum Query)RMQ解决的问题ST算法 O(nlogn)线段树例题数列区间最大值最敏捷的机器人天才的记忆Frequent values总结(ST和线段树对比)RMQ解决的问题 RMQ是一个解决多个区间最值查询的算法,即区间最值查询&…...

一篇文章搞懂Cookie
目录 1 什么是Cookie 2 创建Cookie 3 浏览器查看Cookie 3.1 浏览器查看Cookie的第一种方式 3.2 浏览器查看Cookie的第二种方式 4 获取Cookie 5 修改Cookie 6 Cookie编码与解码 6.1 创建带中文Cookie 6.2 读取带中文Cookie 6.3 获取中文Cookie请求效果 6.4 解决创建和…...