分享在Linux下使用OSGi.NET插件框架快速实现一个分布式服务集群的方法

news2024/11/16 15:40:47

在这篇文章我分享了如何使用分层与模块化的方法来设计一个分布式服务集群。这个分布式服务集群是基于DynamicProxy、WCF和OSGi.NET插件框架实现的。我将从设计思路、目标和实现三方面来描述。

1 设计思路

首先,我来说明一下设计思路。我们先来看看目前OSGi.NET插件框架的服务。在这里,服务不是远程服务,它是轻量级的服务,由接口和实现类组成,如下图所示。服务契约插件定义了服务接口,服务实现插件向服务总线注册服务,服务调用插件利用服务契约(接口)从服务总线获取实现的服务并调用,服务实现插件和服务调用插件都依赖于服务契约,但二者并未有依赖。服务是插件间松耦合的调用方式。

我们希望在不更改现有的通讯机制的情况下,将以前定义的在同一个框架下的服务能够直接不更改代码情况下,变成远程服务。此时,基于远程服务的通讯方式变成如下图所示的方式。

这时候,在不更改服务定义、服务注册的代码下,在OSGi.NET框架中安装一个远程服务宿主插件,它直接将服务总线的服务暴露成远程服务;OSGi.NET插件框架安装一个远程服务客户端插件,就可以使用服务契约来获取并调用远程服务。

接下来,我们希望能更进一步在不需要更改服务定义和注册代码情况下,来实现透明的集群支持。

在这里,我们引入了负载均衡插件。首先,一个OSGi.NET插件框架安装了远程服务负载均衡插件,它管理所有远程服务的负载状况,并为集群提供了统一的访问和负载均衡支持;接着,所有安装了远程服务宿主插件的OSGi.NET框架,会安装一个负载均衡客户端插件,它用于将远程服务注册到负载均衡器;服务调用端安装了远程服务客户端插件,它通过负载均衡器来调用远程服务。

这个思路可以简单描述如下:

A)本地服务 = OSGi.NET插件框架 + 服务契约插件 + 服务实现插件 + 服务调用插件;服务实现和服务调用在同一个OSGi.NET插件框架内,在同一个进程。

B)远程服务实现 = OSGi.NET插件框架 + 服务契约插件 + 服务实现插件 + 远程服务宿主插件,远程服务调用 = OSGi.NET插件框架 + 服务契约插件 + 远程服务客户端插件;服务实现和服务调用可以在不同的OSGi.NET插件框架,在不同进程内。

C)负载均衡器 = OSGi.NET插件框架 + 远程服务负载均衡插件,负载均衡远程服务实现 = OSGi.NET插件框架 + 服务契约插件 + 服务实现插件 + 远程服务宿主插件 + 负载均衡客户端插件,远程服务调用 = OSGi.NET插件框架 + 服务契约插件 + 远程服务客户端插件; 负载均衡器、远程服务、服务调用均可以在不同OSGi.NET插件框架和不同进程,远程服务可以在多台机器中,注册到负载均衡器。

2 设计目标

远程服务和负载均衡的实现基于模块化思路,如下图所示。

(1)不更改本地服务的定义、注册和使用方法;

(2)在本地服务的基础上,安装远程服务宿主插件,服务就暴露成远程服务;

(3)在远程服务的基础上,安装负载均衡客户端插件和负载均衡器,远程服务就支持集群及负载均衡。

以一个简单的服务ISayHelloService为例,下面将描述如何通过以上方式来实现远程服务和服务集群。

2.1 远程服务示例

2.1.1 远程服务注册及实现

如下图所示,SayHelloServiceContract插件定义了一个ISayHelloService服务接口,SayHelloService定义了SayHelloServiceImpl服务实现,在OSGi.NET插件框架安装了UIShell.RemoteServiceHostPlugin插件,这样我们就将本地服务暴露成远程服务了。

下图是SayHelloServiceImpl服务的实现和服务注册。

下图则是服务注册。

你可以发现,为了支持远程服务,我们仅仅是安装了一个远程服务宿主插件,而没有更改服务的实现和注册方法。

2.1.2 远程服务调用

远程服务调用如下所示,在OSGi.NET插件框架安装了远程服务客户端插件。服务调用插件使用服务契约,来调用远程服务。

调用远程服务的步骤为:

1 使用远程服务客户端插件的IRemoteServiceProxyService来获取远程服务;

2 直接调用远程服务的方法。

因此,你可以发现,远程服务的定义和使用都非常的简单。接下来我们再看看负载均衡远程服务的使用。

2.2 负载均衡远程服务示例

2.2.1 负载均衡器

负载均衡器相当于远程服务注册表,它用于注册暴露远程服务的所有机器以及每一个机器每一个服务的负载均衡状况,并提供负载均衡支持。下图是负载均衡器的实现。

负载均衡器向外暴露一个固定的IP地址和端口号,用于注册远程服务,并提供负载均衡。

2.2.2 负载均衡远程服务

支持负载均衡的远程服务需要安装一个负载均衡客户端插件,如下所示。负载均衡客户端插件用于将远程服务注册到负载均衡器,从而,负载均衡器可以来管理远程服务的负载情况,当发生故障时可以实现负载转移和实现负载均衡。

这里,远程负载均衡器客户端插件会连接到负载均衡服务器,向其注册本机器的远程服务。

2.2.3 负载均衡远程服务调用

调用负载均衡远程服务与直接调用远程服务方法类似,如下图所示。它使用GetFirstOrDefaultLoadBalancerService接口来访问负载均衡器,获取经过负载均衡的远程服务。

你可以发现,调用负载均衡远程服务的方法也非常简单。下面,我来介绍一下如何实现。

3 设计实现

首先,我们先来看看远程服务的实现。

3.1 远程服务的实现

远程服务的实现可以归结为以下几点:(1)远程服务宿主插件用于暴露一个WCF服务,这个WCF服务相当于本地服务的Bridge,即客户端对远程服务的调用先中专到这个WCF服务,再由WCF服务来调用本地服务,然后返回给客户端;(2)客户端使用DynamicProxy为服务契约生成一个代理,对这个代理的方法调用将会被拦截,然后调用远程服务宿主插件的WCF服务,将调用结果再返回。

3.1.1 远程服务宿主插件实现

该插件首先定义了一个IRemoteServiceInvoker的WCF服务接口,这个接口及参数的定义如下。

它的作用就是通过调用这个WCF远程服务来调用OSGi.NET框架本地服务,达到将本地服务暴露成远程服务的目的。

这个WCF的实现代码如下所示,其目的就是对WCF的调用转换成对本地服务方法的调用并返回。

 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading;
 using System.Reflection;
 using System.Threading.Tasks;
 using fastJSON;
 using Fasterflect;
 using UIShell.OSGi.Utility;
 
 namespace UIShell.RemoteServiceHostPlugin
 {
     public class RemoteServiceInvoker : IRemoteServiceInvoker
     {
         public static ReaderWriterLock Locker = new ReaderWriterLock();
         public static Dictionary<string, System.Tuple<MethodInfo, Type[], MethodInvoker>> InvokerCache = new Dictionary<string, System.Tuple<MethodInfo, Type[], MethodInvoker>> (); 
 
         public string InvokeService(RemoteServiceInvocation invocation)
         {
             AssertUtility.NotNull(invocation);
             AssertUtility.ArgumentHasText(invocation.ContractName, "service contract name");
             AssertUtility.ArgumentHasText(invocation.MethodName, "service method name");
 
             var service = Activator.Context.GetFirstOrDefaultService(invocation.ContractName);
             string msg = string.Empty;
             if(service == null)
             {
                 msg = string.Format ("Remote Service '{0}' not found.", invocation.ContractName);
                 FileLogUtility.Warn (msg);
                 throw new Exception(msg);
             }
 
             System.Tuple<MethodInfo, Type[], MethodInvoker> invokerTuple;
             using (var locker = ReaderWriterLockHelper.CreateReaderLock (Locker))
             {
                 InvokerCache.TryGetValue (invocation.Key, out invokerTuple);
             }
 
             if (invokerTuple == null)
             {
                 Type serviceType = service.GetType ();
 
                 var serviceMethodInfo = serviceType.GetMethod (invocation.MethodName);
                 if (serviceMethodInfo == null)
                 {
                     msg = string.Format ("The method '{1}' of the remote service '{0}' not found.", invocation.ContractName, invocation.MethodName);
                     FileLogUtility.Warn (msg);
                     throw new Exception (msg);
                 }
 
                 if (invocation.JsonSerializedParameters == null) {
                     invocation.JsonSerializedParameters = new List<string> ();
                 }
 
                 var parameterInfos = serviceMethodInfo.GetParameters ();
                 if (invocation.JsonSerializedParameters.Count != parameterInfos.Length)
                 {
                     msg = string.Format ("The parameters count is not match with the method '{0}' of service '{1}'. The expected count is {2}, the actual count is {3}.", invocation.MethodName, invocation.ContractName, parameterInfos.Length, invocation.JsonSerializedParameters.Count);
                     FileLogUtility.Warn (msg);
                     throw new Exception (msg);
                 }
 
                 var parameterTypes = new Type[parameterInfos.Length];
                 for (int i = ; i < parameterInfos.Length; i++)
                 {
                     parameterTypes [i] = parameterInfos [i].ParameterType;
                 }
 
                 try
                 {
                     var methodInvoker = serviceType.DelegateForCallMethod (invocation.MethodName, parameterTypes);
 
                     invokerTuple = new System.Tuple<MethodInfo, Type[], MethodInvoker> (serviceMethodInfo, parameterTypes, methodInvoker);
 
                     using (var locker = ReaderWriterLockHelper.CreateWriterLock (Locker))
                     {
                         if (!InvokerCache.ContainsKey (invocation.Key))
                         {
                             InvokerCache [invocation.Key] = invokerTuple;
                         }
                     }
                 }
                 catch(Exception ex)
                 {
                     msg = string.Format ("Failed to create delegate method for the method '{0}' of service '{1}'.", invocation.MethodName, invocation.ContractName);
                     FileLogUtility.Warn (msg);
                     FileLogUtility.Warn (ex);
                     throw new Exception (msg, ex);
                 }
             }
 
             var paramters = new object[invokerTuple.Item2.Length];
 
             for(int i = ; i < invokerTuple.Item2.Length; i++)
             {
                 try
                 {
                     paramters[i] = JSON.ToObject(invocation.JsonSerializedParameters[i], invokerTuple.Item2[i]);
                 }
                 catch(Exception ex)
                 {
                     msg = string.Format ("Failed to unserialize the '{0}'th parameter for the method '{1}' of service '{2}'.", i + , invocation.MethodName, invocation.ContractName);
                     FileLogUtility.Warn (msg);
                     FileLogUtility.Warn (ex);
                     throw new Exception (msg, ex);
                 }
             }
 
             try
             {
                 return JSON.ToJSON(invokerTuple.Item3(service, paramters));
             }
             catch(Exception ex)
             {
                 msg = string.Format("Failed to invoke the method '{0}' of service '{1}'.", invocation.MethodName, invocation.ContractName);
                 FileLogUtility.Warn (msg);
                 FileLogUtility.Warn (ex);
                 throw new Exception (msg, ex);
             }
         }
     }
 }

3.1.2 远程服务客户端插件的实现

接下来,我们看看远程服务客户端插件的实现。它定义了一个IRemoteServiceProxyService服务,暴露了两个接口分别用于对远程服务和负载均衡远程服务的调用。

该服务的远程服务获取实现如下所示。

它仅仅时通过DynamicProxy创建远程服务代理类,此时,对代理类方法的调用会转换成远程服务调用。下面看看拦截机的实现。

 class RemoteServiceProxyInterceptor : IInterceptor, IDisposable
 {
     private RemoteServiceContext _remoteServiceContext;
     private RemoteServiceClient _remoteServiceClient;
     public RemoteServiceProxyInterceptor(RemoteServiceContext context)
     {
         _remoteServiceContext = context;
         _remoteServiceClient = new RemoteServiceClient(_remoteServiceContext.IPAddress, _remoteServiceContext.Port);
         _remoteServiceClient.Start();
     }
     public void Intercept(IInvocation invocation)
     {
         try
         {
             var jsonParameters = new List<string>();
             foreach (var param in invocation.Arguments)
             {
                 jsonParameters.Add(JSON.ToJSON(param));
             }
 
             var resultJson = _remoteServiceClient.Invoke(invocation.Method.DeclaringType.FullName, invocation.Method.Name, jsonParameters);
 
             if (!invocation.Method.ReturnType.FullName.Equals("System.Void"))
             {
                 invocation.ReturnValue = JSON.ToObject(resultJson, invocation.Method.ReturnType);
             }
             else
             {
                 invocation.ReturnValue = null;
             }
         }
         catch(Exception ex)
         {
             FileLogUtility.Error (string.Format("Failed to invoke the remote service 'Remote Service: {0}, Method: {1}.'",
                 invocation.Method.DeclaringType.FullName, invocation.Method.Name));
             throw;
         }
     }
 
     public void Dispose()
     {
         if (_remoteServiceClient != null)
         {
             _remoteServiceClient.Stop();
             _remoteServiceClient = null;
         }
     }
 }

拦截机的代码很简单,对远程服务代理类方法的调用将直接转换成对远程服务宿主插件的WCF服务的调用。下面看看负载均衡远程服务的实现。

3.2 负载均衡远程服务的实现

有了以上的技术,关于负载均衡远程服务的实现,就简单多了。负载均衡远程服务的目的就是将所有远程服务统一在负载均衡服务器进行注册,并实现负载的动态管理。因此,需要在远程服务基础上,创建一个负载均衡服务器和负载均衡客户端。负载均衡服务器用于管理所有远程服务及提供远程服务的机器,管理所有远程服务的负载情况,并实现负载均衡及故障转移;负载均衡客户端的目的时将远程服务注册到负载均衡器,并且当远程服务关闭时从负载均衡器卸载。下面,看看负载均衡器的实现。

3.2.1 负载均衡器实现

负载均衡服务器暴露了如下WCF服务。这个服务用于提供远程服务注册和卸载以及负载均衡请求。

这个服务的实现如下所示。

它使用远程服务注册表来实现远程服务的管理和负载均衡的实现。

3.2.2 负载均衡客户端的实现

负载均衡客户端的目的时实现远程服务的注册与卸载,通过该插件将远程服务暴露到负载均衡服务器。这样服务调用者就可以通过负载均衡器来调用远程服务。

3.2.3 负载均衡远程服务调用

负载均衡远程服务的调用方式的实现和远程服务类似。它由远程服务代理服务的GetFirstOrDefaultLoadBalancerService接口来实现。

该接口的实现如下所示,主要时创建代理和方法拦截机。

这个方法调用拦截机会将方法调用转化为:(1)从负载均衡服务器获取均衡的远程服务主机;(2)直接调用该远程服务主机的服务,如果调用失败则尝试进行重新负载均衡。其实现如下所示。

 class RemoteServiceLoadBalancerProxyInterceptor : IInterceptor, IDisposable
 {
     private string LoadBalancerHost
     {
         get
         {
             return ConfigurationSettings.AppSettings["LoadBalancerHost"];
         }
     }
 
     private string LoadBalancerPort
     {
         get
         {
             return ConfigurationSettings.AppSettings["LoadBalancerPort"];
         }
     }
 
     private LoadBalancerContext _remoteServiceLoadBalancerContext;
     private RemoteServiceClient _remoteServiceClient;
     private RemoteServiceLoadBalancerAccessClient _remoteServiceLoadBalancerAccessClient;
     private bool _initialized;
 
     public RemoteServiceLoadBalancerProxyInterceptor(LoadBalancerContext context)
     {
         _remoteServiceLoadBalancerContext = context;
 
         if (string.IsNullOrEmpty(LoadBalancerHost))
         {
             throw new Exception("You need to specified the load balancer host (HostName or IP Address) by app setting 'LoadBalancerHost'.");
         }
 
         int loadBalancerPortInt;
         if (!int.TryParse(LoadBalancerPort, out loadBalancerPortInt))
         {
             throw new Exception("You need to specified the load balancer port by app setting 'LoadBalancerPort'.");
         }
 
         try
         {
             _remoteServiceLoadBalancerAccessClient = new RemoteServiceLoadBalancerAccessClient (LoadBalancerHost, loadBalancerPortInt);
             _remoteServiceLoadBalancerAccessClient.Start ();
         }
         catch(Exception ex)
         {
             FileLogUtility.Error (string.Format("Faild to connect to load balancer '{0}'.", _remoteServiceLoadBalancerContext));
             FileLogUtility.Error (ex);
             throw;
         }
     }
 
     private bool Initialize(string serviceContractName)
     {
         if(_remoteServiceClient != null)
         {
             _remoteServiceClient.Stop();
         }
 
         RemoteServiceHost remoteHost = null;
         try
         {
             remoteHost = _remoteServiceLoadBalancerAccessClient.Balance(serviceContractName);
             FileLogUtility.Inform(string.Format("Get the remote service host '{0}' by load balancer '{1}'.", remoteHost, _remoteServiceLoadBalancerContext));
         }
         catch(Exception ex)
         {
             FileLogUtility.Error (string.Format("Faild to get a remote service host by load balancer '{0}'.", _remoteServiceLoadBalancerContext));
             FileLogUtility.Error (ex);
             return false;
         }
         if (remoteHost != null)
         {
             _remoteServiceClient = new RemoteServiceClient (remoteHost.IPAddress, remoteHost.Port);
             try
             {
                 _remoteServiceClient.Start ();
                 return true;
             }
             catch(Exception ex)
             {
                 FileLogUtility.Error (string.Format("Failed to connect to the remote service host '{0}' by using load balancer '{1}'.", remoteHost, _remoteServiceLoadBalancerContext));
             }
         }
 
         return false;
     }
 
     public void Intercept(IInvocation invocation)
     {
         var serviceContractName = invocation.Method.DeclaringType.FullName;
         if (!_initialized)
         {
             _initialized = Initialize (serviceContractName);
             if (!_initialized)
             {
                 invocation.ReturnValue = null;
                 return;
             }
         }
 
         var jsonParameters = new List<string>();
         foreach (var param in invocation.Arguments)
         {
             jsonParameters.Add(JSON.ToJSON(param));
         }
 
         int tryTimes = ;
 
         for (int i = ; i < tryTimes; i ++ )
         {
             try
             {
                 var resultJson = _remoteServiceClient.Invoke(serviceContractName, invocation.Method.Name, jsonParameters);
 
                 if (!invocation.Method.ReturnType.FullName.Equals("System.Void"))
                 {
                     invocation.ReturnValue = JSON.ToObject(resultJson, invocation.Method.ReturnType);
                 }
                 else
                 {
                     invocation.ReturnValue = null;
                 }
                 return;
             }
             catch(Exception ex)
             {
                 FileLogUtility.Error (string.Format("Failed to invoke the remote service 'Remote Service: {0}, Method: {1}.'",
                     serviceContractName, invocation.Method.Name));
                 FileLogUtility.Error (ex);
                 if (i == tryTimes)
                 {
                     throw;
                 }
                 if (!((_initialized = Initialize (serviceContractName)) == true))  // 重新Balance
                 {
                     throw;
                 }
             }
         }
     }
 
     public void Dispose()
     {
         if (_remoteServiceClient != null)
         {
             _remoteServiceClient.Stop();
             _remoteServiceClient = null;
         }
     }
 }

4 小结

在这篇文章,我详细介绍了支持集群的远程服务的实现。你可以发现,整体实现完全按照模块化组装的方式。你也可以尝试来考虑以模块化组装的方法实现一个远程服务集群。多谢支持~~。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/711940.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++中生成二维码-libqrencode

文章目录 前言libqrencode在qt中调用libqrencode其他 前言 二维码的种类很多。本文仅介绍&#xff0c;如何用C生成QR码(QRcode)。通常而言&#xff0c;我们不需要知道QR码的详细结构&#xff0c;如QrCode的结构原理与实战 | 张展鹏的博客。我们只需要&#xff0c;可以将文本转…

Python——— 字符串

&#xff08;一&#xff09;字符串 字符串是 Python 中最常用的数据类型。我们可以使用引号 ( 或 " ) 来创建字符串。顾名思义&#xff0c;羊肉串是由羊肉做成的串&#xff0c;而字符串就是由字符组成的。 字符串的本质是&#xff1a;字符序列。 2 Python 不支持单字符…

Kafka 小结

Kafka 是由 Linkedin 开发并开源的分布式消息系统&#xff0c;因其分布式及高吞吐率而被广泛使用&#xff0c;现已与 Cloudera Hadoop、Apache Storm、Apache Spark、Flink 集成。 Kafka 使用场景 页面访问量 PV、页面曝光 Expose、页面点击 Click 等行为事件&#xff1b;实时计…

低分辨率图像中目标检测(附论文下载)

关注并星标 从此不迷路 计算机视觉研究院 公众号ID&#xff5c;ComputerVisionGzq 学习群&#xff5c;扫码在主页获取加入方式 论文地址&#xff1a;https://arxiv.org/pdf/2201.02314.pdf 计算机视觉研究院专栏 作者&#xff1a;Edison_G 超分辨率&#xff08;SR&#xff09;等…

【Openvino03】深入了解OpenVINO™ 工具包与Jupyter Notebooks工程

接上一篇&#xff0c;本篇将以OpenVINO™ 工具包、Jupyter Notebook工具以及OpenVINO™ Notebooks工程为基础&#xff0c;依照构建环境、工具学习、案例学习、实战部署的顺序引导初学者完成从0到1学习人工智能的全过程&#xff0c;希望众多对人工智能感兴趣的开发者&#xff0c…

【多维BFS】ABC308 D

VP的时候居然花了半小时&#xff01; 可恶&#xff01; D - Snuke Maze (atcoder.jp) 题意&#xff1a; 思路&#xff1a; 首先&#xff0c;我们发现到达一个格子之后&#xff0c;下一个格子的字符是确定的 但是&#xff0c;下一个格子到底是哪个是不确定的 下一个格子不…

FreeRTOS源码解析——第一章 整体架构

FreeRTOS源码解析 第一章 FreeRTOS 整体架构 第二章 FreeRTOS 编程规范 第三章 FreeRTOS 内存管理 第四章 FreeRTOS 任务管理 第五章 FreeRTOS 消息队列 第六章 FreeRTOS 软件定时器 第七章 FreeRTOS 信号量 第八章 FreeRTOS 互斥量 第九章 FreeRTOS 任务通知 第十章 FreeRTOS…

算法习题之宏观分层问题

宏观分层 习题1 给定一个正方形矩阵matrix&#xff0c;原地调整成顺时针90度转动的样子习题2 给定一个长方形矩阵matrix&#xff0c;实现转圈打印习题3 给定一个正方形或者长方形矩阵matrix&#xff0c;实现zigzag打印习题4 输入N,在控制台上输出初始边长为N的图案 习题1 给定一…

U盘数据丢失是什么原因?轻松让U盘数据恢复的教程

在数字化时代&#xff0c;我们不可避免地使用各种便携式存储设备&#xff0c;如U盘&#xff0c;来传输和存储重要数据。然而&#xff0c;有时我们可能不小心删除了U盘中的文件&#xff0c;或者格式化了U盘等等而导致数据丢失。这种情况下&#xff0c;你可能会困惑地想知道&…

机器学习——基于Tensorflow和Keras实现卷积神经网络CNN——猫狗分类

文章目录 环境的配置神经网络CNN的介绍卷积前馈神经网络卷积神经网络应用邻域 数据集准备数据预处理构建基准模型 总结什么是过拟合&#xff08;overfit&#xff09;&#xff1f;什么是数据增强&#xff1f;单独制作数据增强&#xff0c;精确率提高了多少&#xff1f; 然后再添…

硬件大熊原创合集(2023/06更新)

06月份更新篇章&#xff1a; 一款射频芯片的layout设计指导案例-篇章1 一款射频芯片的layout设计指导案例-篇章2 警惕超声波工艺对晶振造成损伤 走嵌入式方向&#xff0c;一定要软硬件都懂吗&#xff1f; 6月份广州光亚展&#xff0c;和电子电力大咖文老师见了个面&#xff0c;…

2023车载摄像头9大热点趋势

摘要&#xff1a; 相较于消费类电子摄像头&#xff0c;车载摄像头的工作环境极度恶劣&#xff0c;比如说震动、高温、雨雾、低温、光线变化剧烈等。而车载摄像头以驾驶安全为目的&#xff0c;上述各个工作状态下&#xff0c;均需要能获取稳定、可靠、清晰的周边环境数据。 近年…

HOT36-二叉树的中序遍历

leetcode原题链接&#xff1a;二叉树的中序遍历 题目描述 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3,2]示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a…

BUUCTF藏藏藏

也是一个图片文件&#xff0c;和一个txt文件 txt文件中是一个提交flag格式&#xff0c;没啥用 拿去010打开后发现可能存在隐藏文件 这个格式说明还有解压文件吧&#xff0c;但是为啥分离不出来呢 刚好有另外一个分离文件的软件foremost 解压后打开在zip文件下有一个福利docx文件…

【零基础入门学习Python---Python网络编程保姆级教程】

&#x1f680; Python &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

GDB寻找segmentation fault位置

一、在当前文件夹下生成指定二进制文件的core文件 查看允许core文件空间大小&#xff1a; ulimit -c修改core文件空间为无限大&#xff1a; ulimit -c unlimited让core文件生成在当前目录下&#xff1a; sudo bash -c echo core.%e.%p > /proc/sys/kernel/core_pattern再…

2023黑马头条.微服务项目.跟学笔记(二)

2023黑马头条.微服务项目.跟学笔记 二 app端文章查看&#xff0c;静态化freemarker,分布式文件系统minIO今日简介学习内容1.文章列表加载1.1 需求分析1.2 表结构分析思考:表的垂直拆分 1.3 导入文章数据库1.3.1 导入数据库1.3.2 导入对应的实体类总结 1.4 实现思路1.4.1 sql练习…

hippo - 提升我们团队工程效率的工具

1. 背景 我们 shopeepay 团队是一个比较大的前端团队&#xff08;80成员&#xff09;&#xff0c;团队内部分布着 react native、javascript npm包、react component npm包、serverless、普通的react工程等多种类型的项目&#xff0c;每种项目类型中的每个项目都有它们自己的配…

Windows 使用Git使用Gitee仓库

1.本地创建文件夹。图中例子&#xff0c;我在桌面创建了文件夹。 2.打开 Git Bash Here. 3.Git Bash 的部分指令。 ls 查看当前文件夹 mkdir gittest01 创建文件夹 cd gittest01 进入文件夹 git init 创建本地仓库 git config --global user.name"chengqian" 创建者…

Redis主从复制模式的2

搭建完后可以去测试一下这几个问题: 1.主机中的数据会复制到从节点中 2.主机中新增的数据会同步到从节点中 3.redis集群后,从节点只能读(slave),主节点可以写(master) 4.主机重启后还是主节点 5.从机重启后也会变成主节点 血脉相传 一个主机master理论上可以有多个从机slave…