【大数据之Hadoop】从自定义 RPC 到 Hadoop RPC ,理解分布式通信系统的底层工作原理

news2024/9/27 23:24:06

1. 前言

Hadoop是分布式计算系统,在分布式环境中,网络通信模块是其核心模块之一。要学好Hadoop,需理解其底层通信系统的基本工作原理。Hadoop提供有体系完整的RPC框架,实现了对底层网络通信过程的优雅封装。

本文将从RPC概念说起,一起聊聊Hadoop RPC的实现细节。

先理解什么是RPC

RPC中的R是单词Remote的首字母,PProcedure 的首字母,CCall首字母。

翻译过来:远程过程调用。 如果仅是翻译一下,说了等于没有说。

如需彻底理解RPC,则需理解过程的 含义:

  • 过程可以认为是方法函数,甚至可以认为是一个对象子程序。为了简化问题,本文所说过程方法

同进程中方法之间的调用,称为本地调用

那么,是否可以认为发生在不同进程之间的调用就是远程调用,广义而言,如果认为是指不在同一个进程中,这么说也没有什么不对。

Tips: 狭义而言,远程调用指物理位置不同的计算机内进程之间方法的调用。如分布式、微服务、B/S……环境。

不同进程之间如何实现过程的调用?

答案是:使用网络通信模块实现。

可以称:通过底层网络通信模块实现的不同进程间的过程调用就是远程调用。所以说,远程调用是一个广义上的概念。套用一个广告语:不是所有的牛奶都叫特仑苏,但是特仑苏指的是牛奶。同理,不是所有的远程调用都叫PRC,但是RPC一定是远程调用。

什么样的远程调用才叫RPC?则需要从远程调用的底层流程说起。

2. 原生网络通信

什么是原生网络通信?

从一个问题开始。

如果现在有一个进程A,它需要一个业务逻辑功能,发现进程B里面有。便心念:能否借用B进程的方法用一用?

1.png

想法很美好,但毕竟不是自己家的,所以需要一些方法和措施。

为了便于理解,另举一个现实生活中的例子:比如你想借邻居家的洗衣机洗衣服。想想,你会怎么办?对了,假设你隔壁住的是一个很友好的邻居。

常规的操作流程是不是应该如下:

  • 首先,你来到邻居家门前,开始敲门。
  • 邻居帮你把门打门。
  • 你发出请求:您好,能不能借你家的洗衣机洗几件衣服。
  • 中国好邻居说:可以的,你可以把衣服拿过来,我帮你先洗着。
  • 你把自己的衣服打包交给邻居。
  • 邻居拆开你的包裹,把衣服丢到洗衣机里。
  • 邻居把衣服洗完后,把洗好的衣服打包后交给你。
  • 最后,你取走衣服后,别忘记说声谢谢。

不同进程之间方法的调用,和你借邻居家洗衣机洗衣服的流程差不多。只是进程之间的敲门开门需要使用计算机语言提供的网络编程 API

其流程大致如下:

  • B进程先建立一个socket监听器。B进程必须是一个非常热心的进程,随时候命等待别的进程的敲门。
  • A进程向B进程发生网络连接请求,得到的B的应答后,两者便建立起了网络连接。类似于你家的邻居开门了。
  • A把自己的数据(类似于衣服)打包后向B进程发起处理请求(类似于洗衣请求)。
  • B进程接收到你的包裹,解开包裹,并把A的数据交给自己的方法进行处理。
  • B的方法处理完成后,B会把处理结果打包,再通过网络通信发送给A

可以使用JAVA语言提供的API实现如上流程。完整代码如下:

  • B程序代码:B程序是服务提供者,可称B为服务器组件。
package com.gk.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/*
* 服务提供者
*/
public class B {
	/*
	 * B 中的方法,也是 A 需要的
	 */
	static String hello(String name) {
		return "Hello!" + name;
	}
    /*
    * B端的网络通信
    * 本质 B 就是服务器 socket
    */
	public static void main(String[] args) throws IOException {
		// 监听请求
		ServerSocket serverSocket = new ServerSocket(1234);
		// 等待网络连接
		Socket socket = serverSocket.accept();
		// 接受 A 传递过来的数据
		InputStream inputStream = socket.getInputStream();
		byte buffers[] = new byte[20];
		int read = inputStream.read(buffers);
		String name = new String(buffers, 0, read);
        //调用自己的方法,成全 A 的远程调用
		String info = hello(name);
		// 把处理结果传递给 A
		OutputStream outputStream = socket.getOutputStream();
		outputStream.write(info.getBytes());
		inputStream.close();
		outputStream.close();
		socket.close();
		serverSocket.close();
	}
}
  • A程序的代码:A是需要服务的一端,可称 A为客户端程序。
package com.gk.clien;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
/*
* 客户端
*/
public class A {
	public static void main(String[] args) throws UnknownHostException, IOException {
		// 发起网络连接
		Socket socket = new Socket("localhost", 1234);
		// 准备数据
		String name = "rose";
		// 把数据传递给 B
		OutputStream outputStream = socket.getOutputStream();
		outputStream.write(name.getBytes());
		// 接受 B 处理好后的数据
		InputStream inputStream=socket.getInputStream();
		byte[] buffers=new byte[20];
		int read= inputStream.read(buffers);
		//输出
		System.out.println(new String(buffers,0,read));
		outputStream.close();
		inputStream.close();
		socket.close();
	}
}
  • 测试:先执行B程序,再执行 A程序。在A端可以看到 B端处理后的数据。

2.png

本质上,AB的程序结构就是基于通信机制的C/S结构。

至此,应该会有一个疑问?

如果AB经常会有如此的请求,或者除了A还有更多的进程需要请求 B。类似于你经常要借邻居家的洗衣机洗衣服。

会发现每次都要经过敲门、开门等一系列繁琐的流程。而实际上,每一次唯一变化的是要洗的衣服。你如何才能简化这些流程,让的过程变的具有艺术性。

现实生活中,你可以请一个代理人。自然,邻居也可以请一个代理人。把自身从繁琐的流程中解脱出来。

Tips: 需要明白,请代理人只是简化了请求者的工作量,并没有减少实际流程。

同理,进程间通信时,也可以请代理人。这里的代理者,只是不是人而是组件。

至此回答什么是原生网络通信?

基于原生API,老老实实、按部就班地实现网络通信称为原生网络通信。

刚说过,可以使用代理者的模型实现网络通信,其本质就是封装理念。

3. 代理者模式

代理者模式的基本思想:

  • 把原生通信系统中的公共流程封装在特定的组件中。
  • AB分别设计一个代理者。
  • A或其它进程需要B进程的服务时,只需要把数据传递给代理者组件,然后舒适地等待代理者把B处理的结果返回给自己。
  • B同样由自己的代理组件负责接收 A或其它进程传递过来的数据,并正确调用自己的方法且返回数据处理结果。

于此,A的高层业务组件和B的业务服务组件便可从无聊而不得不面对的流程中解脱出来,可全心全意关注自己的高层业务。本质就是基于单一职责思想的解耦操作。

3.png

基于代理者思想,现在开始自定义简易版的远程请求框架。

  • 首先,B程序需要以接口的方式告诉需求者自己能提供什么样的功能。类似于公司发布招聘信息,需要清晰告诉求职者什么样的岗位有什么样的具体要求。然后 服务需求者(A)需要明白B的岗位要求,并签订严格的劳务合同,明确自己的责任。

    这里的接口便是协议,约束供需双方的行为规范。

package com.gk.protocol;
/*
 * 通信双方共同遵守的行为准则
 */
public interface MyProtocol {
	  String hello(String name);
}
  • A 程序至少有 2 个独立组件:

    **业务组件:**通俗而言,具体做些事情。

    **代理组件:**当业务组件有远程调用请求时,由代理组件去实施。

代理组件本质是遵循代理设计模式设计出来的组件,这里使用 javaproxy类动态生成代理组件。代理组件本身不能提供具体实现,而是封装了网络API,以此访问指定主机上的功能模块。

先编写代理组件:

package com.gk.clien;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
import com.gk.protocol.MyProtocol;
/*
 *  A 的代理组件,
 *  功能,代替服务需求组件访问 B 提供的功能。
 *  代理组件必须实现 B 定义的接口以此了解 B 提供的功能。
 *  如果对方有什么功能都不知道,代理者是不合格的
 */
public class AProxy implements InvocationHandler {
	// 远程计算机的 ip
	private String ip;
	// 远程计算机的端口
	private int port;
    
	public AProxy(String ip, int port) {
		this.ip = ip;
		this.port = port;
	}

	/*
	 * 创建动态代理组件
	 */
	MyProtocol createProxy() {
		//基于 B 程序的接口定义动态创建代理者
		MyProtocol myProtocol = (MyProtocol) Proxy.newProxyInstance(AProxy.class.getClassLoader(),
				new Class[] { MyProtocol.class }, this);
		return myProtocol;
	}

	/*
	 * 封装具体的网络请求
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// 发起网络连接
		Socket socket = new Socket(this.ip, this.port);
		// 把数据传递给 B
		OutputStream outputStream = socket.getOutputStream();
		// 把参数和方法名传递过去。设计一个简单的字符串格式的通信协议,最好使用 json 数据格式
		StringBuffer info = new StringBuffer(method + "\t");
		for (Object arg : args) {
			// 参数之间使用逗号隔开
			info.append(arg).append(",");
		}
		info.deleteCharAt(info.length() - 1);
		info.append("]");
		outputStream.write(info.toString().getBytes());
		// 接受 B 处理好后的数据
		InputStream inputStream = socket.getInputStream();
		byte[] buffers = new byte[20];
		int read = inputStream.read(buffers);
		// 转换成字符串
		String res=new String(buffers, 0, read);
		outputStream.close();
		inputStream.close();
		socket.close();
		return res;
	}
}

编写A 的业务组件:

package com.gk.clien;
import com.gk.protocol.MyProtocol;
/*
 * A 的业务组件
 */
public class AService {
    //依赖 B 接口中定义的功能
	private MyProtocol myProtocol;
	public AService(MyProtocol myProtocol) {
		this.myProtocol = myProtocol;
	}
	/*
	 * 业务方法
	 */
	public void doSomething(String name) {
		// 自己的业务
		System.out.println("自己能实现的业务");
		// 另一部分业务需要远程调用
		String res = this.myProtocol.hello(name);
		System.out.println("远程业务功能模块处理结果:" + res);
	}
}
  • B也应该有 2 个组件。

    B向外服务的业务组件。

    B 的代理者。

编写B的业务组件: 就是对自己接口定义的实现。

package com.gk.server;
import com.gk.protocol.MyProtocol;
/*
* 需要实现自己定义的接口
*/
public class BService implements MyProtocol {
	@Override
	public String hello(String name) {
		return "Hello!" + name;
	}
}

编写 B 的代理者组件: 的代理者主要是解析A传递过来的数据,并使用反射方式动态调用业务模块的功能。理论而言,B应该提供网络连接和网络响应组件。因本文仅是为了讲明白远程调用概念,故突出主要的,忽略次要的。

package com.gk.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import com.gk.protocol.MyProtocol;

public class BProxy {
    //真正实现了接口的组件
	private MyProtocol myProtocol;
	public BProxy(MyProtocol myProtocol) {
		this.myProtocol = myProtocol;
	}
    /*
    * 方法中代码有 3 层功能:
    *  A、网络连接
    *  B、解析数据
    *  C、处理数据并返回数据
    * 理论而言,为了响应多用户请求,需使用多线程机制,且需把上述三部分功能设计到 3 个组件中
    */
	void getRes() throws IOException, NoSuchMethodException, SecurityException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException {
		// 监听请求
		ServerSocket serverSocket = new ServerSocket(1234);
		// 等待网络连接
		Socket socket = serverSocket.accept();
		// 接受 A 传递过来的数据
		InputStream inputStream = socket.getInputStream();
		byte buffers[] = new byte[100];
		int read = inputStream.read(buffers);
         //得到请求数据
		String info = new String(buffers, 0, read);
         //解析请求数据
		String[] strs = info.split("\t");
         //方法名
		String methodName = strs[0];
		//解析参数
		String args[] = strs[1].split(",");
		Class<?> clz = MyProtocol.class;
         // 利用反射机制,根据请求者提供的数据调用相关方法
		Method method = clz.getMethod(methodName, new Class[] { String.class });
		String res = String.valueOf(method.invoke(this.myProtocol, args));
		// 返回给 A
		OutputStream outputStream = socket.getOutputStream();
		outputStream.write(res.getBytes());
		inputStream.close();
		outputStream.close();
		socket.close();
		serverSocket.close();
	}
}
  • 测试:

    B端的测试代码:

package com.gk.server;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
public class B {
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException {
        //调用代理者
		BProxy bProxy = new BProxy(new BService());
		bProxy.getRes();
	}
}

A 端的测试代码:

package com.gk.clien;
public class A {
	public static void main(String[] args)  {
		//代理对象
		AProxy aProxy=new AProxy("127.0.0.1",1234);
		//业务组件
		AService aService=new AService(aProxy.createProxy());
		//业务实现
		aService.doSomething("world");
	}
}

先执行 B 端测试代码,再测试 A 端代码。输出结果:

4.png

借助代理者思想,对原生网络通信的封装代码,可以让 A程序在不了解底层网络通信细节基础上,实现对B程序中功能模块的访问。每次调用时,只需把数据传递给代理者便可,大大简化了远程调用的过程。

这也是RPC的目标。那么原生网络通信,以及自定义的远程调用框架,是否可以称为RPC

回归到RPC的概念上。

RPC本质是一个思想,或一个协议。提供了统一封装原生网络通信的标准,这个标准也称为RPC协议。在RPC协议或标准中无论是客户端还是服务端,都有一个叫 stub的程序,类似于代理者。其访问流程如下:

  • 客户程序以本地方式调用系统产生的Stub程序;
  • Stub程序将函数调用信息按照网络通信模块的要求封装成消息包,并交给通信模块发送到远程服务器端。
  • 远程服务器端接收此消息后,将此消息发送给相应的Stub程序;
  • Stub程序拆封消息,形成被调过程要求的形式,并调用对应函数;
  • 被调用函数按照所获参数执行,并将结果返回给Stub程序;
  • Stub程序将此结果封装成消息,通过网络通信模块逐级地传送给客户程序

Tips: RPC是思想、规范,基于PRC规范实现的具有远程调用的程序称为RPC框架。所以PRC在实现层面上各有差异性。

如此看来,纯原生的网络通信不能算是RPC,而基于代理者思想的自定义远程访问框架可以算是简陋版的RPC实现。

Tips: j2eeservlet规范,本质也是一个远程调用规范,而其接口规范便是 http协议。tomcat和基于serlvt规范编写的web程序就是远程调用的实例。

4. Hadoop RPC

4.1 特点及结构

Hadoop RPC实际上是分布式计算中C/S(Client/Server)模型的一个应用实例,对于Hadoop RPC而言,它具有以下几个特点。

  • 透明性:封装底层网络通信,简化高层业务组件的调用需求,目的就让客户端调用服务器端子程序时,如同本地调用一样。
  • 高性能Hadoop各个系统(如HDFS、YARN、MapReduce等)均采用了Master/Slave结构,其中,Master本质上是一个RPC Server,负责响应、处理Slave发送的请求,为了保证Master的并发处理能力,RPC Server必须是高性能服务器。
  • 可控性JDK中已经自带了一个RPC框架—RMI(Remote Method Invocation,远程方法调用),但RMI体量过大且不易控制。Hadoop尽可能重新实现,满足轻量级效果。

Hadoop RPC由采用四层体系结构设计:

  • 序列化层:为了方便跨机器传输数据,Hadoop会将各种数据序列化成字节流后在网络中传输。
  • 函数调用层:函数调用层本质是使用动态代理实现远程调用。
  • 网络传输层:基于 socket,实现客户端和服务器端的真正意义上的数据交互。
  • 服务器端处理层:让服务器具有并发处理能力。hadoop采用基于Reactor设计模式的事件驱动 I/O模型。

5.jpg

4.2 使用 Hadoop RPC

Hadoop 与 RPC有关的主要功能代码均封装在 RPC类中:

org.apache.hadoop.ipc.RPC

主要方法介绍:

  • getProxy/waitForProtocolProxy:构造客户端代理对象(该对象实现了某个协议),用于向服务器发送RPC请求。
public static ProtocolProxy <T>    public static <T> T getProxy(Class<T> protocol,
                                long clientVersion,
                                InetSocketAddress addr, Configuration conf,
                                SocketFactory factory) throws IOException{}
public static <T> ProtocolProxy<T> waitForProtocolProxy(Class<T> protocol,
                               long clientVersion,
                               InetSocketAddress addr, Configuration conf,
                               int rpcTimeout,
                               RetryPolicy connectionRetryPolicy,
                               long timeout) throws IOException { }
  • RPC.Builder:为某个协议(实际上是Java接口)实例构造一个服务器对象,用于处理客户端发送的请求。使用 Hadoop RPC 可以定制自己的网络请求模型。
public static Server RPC.Builder (Configuration).build()

hadoop rpc除了代码设计上的艺术性和优雅性以及结构上的层次性。hadoop rpc相关功能模块在上文自定义框架中都可以找到对应项。现在使用hadoop rpcAPI同样实现 hello 功能请求。会发现整个过程和自定义框架中的实现流程大同小异。

  • 首先自定义服务端的PRC协议,需要继承VersionedProtocol
package com.hc.rpc;
import java.io.IOException;
import org.apache.hadoop.ipc.VersionedProtocol;
/*
* 功能定义
*/
interface MyProtocol extends VersionedProtocol {
	// 版本号,默认情况下,不同版本号的RPC Client和Server之间不能相互通信
	public static final long versionID = 1L;
	String hello(String name) throws IOException;
	int add(int num1, int num2) throws IOException;
}
  • 实现RPC协议。Hadoop RPC协议只是一个接口,需要实现该接口提供实际功能。
package com.hc.rpc;
import java.io.IOException;
import org.apache.hadoop.ipc.ProtocolSignature;
/*
* 功能实现类
*/
public class MyProtocolmpl implements MyProtocol {
	// 重载的方法,用于获取自定义的协议版本号,
	public long getProtocolVersion(String protocol, long clientVersion) {
		return MyProtocol.versionID;
	}

	// 重载的方法,用于获取协议签名
	public ProtocolSignature getProtocolSignature(String protocol, long clientVersion, int hashcode) {
		return new ProtocolSignature(MyProtocol.versionID, null);
	}

    /*
    *对外的服务方法
    */
	@Override
	public String hello(String name) throws IOException {
		return "hello" + name;
	}
    /*
    *对外的服务方法
    */
	@Override
	public int add(int num1, int num2) throws IOException {
		return num1 + num2;
	}
}
  • 构造并启动RPC Server。类似于 B程序。使用静态类Builder构造一个RPC Server,并调用start()启动该Server
package com.hc.rpc;
import java.io.IOException;
import org.apache.hadoop.HadoopIllegalArgumentException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;
import org.apache.hadoop.ipc.RPC.Server;
/*
* 服务提供者
*/
public class HadoopServer {
	public static void main(String[] args) throws HadoopIllegalArgumentException, IOException {
		Configuration conf = new Configuration();
        /*
        * BindAddress和Port分别表示服务器的host和监听端口号。
        * NnumHandlers 表示服务器端处理请求的线程数目。
        * 到此为止,服务器处理监听状态,等待客户端请求到达。
        */
		Server server = new RPC.Builder(conf).setProtocol(MyProtocol.class).setInstance(new MyProtocolmpl())
				.setBindAddress("127.0.0.1").setPort(1234).setNumHandlers(5).build();
		server.start();
	}
}
  • 构造RPC Client并发送RPC请求(类似于 A 程序)。使用静态方法getProxy构造客户端代理对象,直接通过代理对象调用远程端的方法,具体如下所示:
package com.hc.rpc;
import java.io.IOException;
import java.net.InetSocketAddress;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;

public class Client {
	public static void main(String[] args) throws IOException {
		Configuration conf = new Configuration();
        //动态代理组件
		MyProtocol proxy = (MyProtocol) RPC.getProxy(MyProtocol.class, MyProtocol.versionID,
				new InetSocketAddress("127.0.0.1", 1234), conf);
         //远程调用
		int result = proxy.add(5, 6);
		System.out.println(result);
        //远程调用
		String res = proxy.hello("world");
		System.out.println(res);
	}
}
  • 测试,先启动服务端程序,再启动客户端程序。

5.png

4. 总结

RPC是对远程访问的一种架构思想。用来简化客户端远程请求模式。

Hadoop rpc是基于RPC思想的RPC构架实例,因此构架用于分布式计算环境中,需要服务器快速、并行地响应多用户的请求,且要保证数据的安全性和健壮性。所以了解其原理以及读懂源代码,可以让使用者在使用hadoop时更有通透性。

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

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

相关文章

从五一的旅游热潮看,该如何实现数字文旅的转型升级?

文旅产业是文化和旅游行业的融合&#xff0c;属于国家战略性产业&#xff0c;资源消耗低&#xff0c;带动系数大&#xff0c;就业机会多&#xff0c;综合效益好。改革开放以来&#xff0c;随着国内工业化、城镇化的快速发展&#xff0c;日益增长的大众化、多样化消费需求为文旅…

java电子招标采购系统源码:营造全面规范安全的电子招投标环境,促进招投标市场健康可持续发展

营造全面规范安全的电子招投标环境&#xff0c;促进招投标市场健康可持续发展 传统采购模式面临的挑战 一、立项管理 1、招标立项申请 功能点&#xff1a;招标类项目立项申请入口&#xff0c;用户可以保存为草稿&#xff0c;提交。 2、非招标立项申请 功能点&#xff1a;非招标…

JUC多并发编程 对象内存布局

对象的内存布局简介 在 Hotspot 虚拟机里&#xff0c;对象在堆内存中存在布局可划分为三个部分: 对象头(Header), 实例数据(Instance Data) 和对齐填充(Padding 保证8字节位数) 对象头 对象标记 MarkWord, 在64位操作系统中&#xff0c; Mark Word 占8个字节, 类型占 8个字节&…

单词搜索I II/前缀树

79单词搜索 思路&#xff1a; 注意&#xff1a; 我自己在写 for i in range(m):for j in range(n):# 对每一个格子都从头开始搜索if self.__search_word(board, word, 0, i, j, marked, m, n):return True这一段的时候&#xff0c;就写成了&#xff1a; 这一段代码是…

Mysql 主从 读写分离

目录 0 课程视频 1 概述 1.1原理 -> 传二进制日志到 从库 -> 执行 1.2 作用 1.2.1 主库崩 从库上 1.2.2 读写分离 1.2.3 从库备份 -> 备份完 再用 二进制日志同步 2 搭建 2.1 防火墙 端口号开启 2.2 主库设置 2.2.1 修改配置文件 -.> /etc/my.cnf 2.2…

高压放大器模块在平面水声压电换能器研究中的应用

实验名称&#xff1a;平面水声压电换能器的制备与性能研究 研究方向&#xff1a;压电换能器 实验原理&#xff1a; 压电换能器是能够发射和接收超声波的电声转换器件&#xff0c;按照不同的标准&#xff0c;换能器可分为不同的种类。按照功能可分为发射型、接收型和收发两用…

亚马逊下单需要什么条件?(养号干货知识)

混迹亚马逊的老手都知道&#xff0c;测评向来都是最有效&#xff0c;最快速的推广方式&#xff0c;给自己商品做点销量&#xff0c;优化一下listing&#xff0c;留一下一些优质评论&#xff0c;甚至于打压一下竞品&#xff0c;帮自己商品解个围。 养号环境四大步&#xff1a; …

定制海报、AI扣人像小程序

大家好,我是csdn的博主:lqj_本人 这是我的个人博客主页: lqj_本人的博客_CSDN博客-微信小程序,前端,python领域博主lqj_本人擅长微信小程序,前端,python,等方面的知识https://blog.csdn.net/lbcyllqj?spm=1011.2415.3001.5343哔哩哔哩欢迎关注:小淼Develop 小淼Develop的个…

nakamichi车机中控屏密码破解(开发者选项密码、usb主从模式切换密码)

版本 Android11 平台 mtk 打开开发者选项&#xff0c;连续点击系统版本号会弹出密码框确认 这个默认密码这台设备并没有配置&#xff0c;而是动态的 默认密码 SystemProperties.get(“ro.sys.developer”, “”); 动态根据设备当前日前来生成的格式为 yyyy.MM.dd 所以上面的…

Node框架 【Koa】开发框架、路由

文章目录 &#x1f31f;前言&#x1f31f;开发框架&#x1f31f;应用程序&#x1f31f;应用程序Koa类&#x1f31f;应用对象(app)的方法&#x1f31f;app.use(function)&#x1f31f;app.listen(...) &#x1f31f;应用程序设置 &#x1f31f;上下文(Context) &#x1f31f;路由…

电脑c盘满了变成红色了怎么清理?看看这7个方法

当C盘出现红色标识提示时&#xff0c;表示该分区的可用存储空间已经极度不足&#xff0c;可能会影响系统稳定性和性能。如果您需要清理C盘并腾出更多的存储空间&#xff0c;可以采取以下措施&#xff1a; 一、电脑c盘7种清理方法 方法1&#xff1a;清空回收站 演示机型&#…

pytorch优化器——add_param_group()介绍及示例、Yolov7 优化器代码示例

系列文章目录 基础函数2——enumerate()、hasattr()、isinstance() pytorch学习率设置——optimizer.param_groups、对不同层设置学习率、动态调整学习率。 文章目录 系列文章目录前言1、关于pytorch优化器2、add_param_group()3、pytorch优化器4、pytorch优化器测试总代码5、…

如何保证 RabbitMQ 的消息可靠性

前言 项目开发中经常会使用消息队列来完成异步处理、应用解耦、流量控制等功能。虽然消息队列的出现解决了一些场景下的问题&#xff0c;但是同时也引出了一些问题&#xff0c;其中使用消息队列时如何保证消息的可靠性就是一个常见的问题。如果在项目中遇到需要保证消息一定被…

大数据技术之Hadoop-入门

第1章 Hadoop概述 1.1 Hadoop是什么 分布式&#xff1a;多台服务器共同完成某一项任务。 1.2 Hadoop发展历史 1.3 Hadoop三大发行版本 Hadoop三大发行版本&#xff1a;Apache、Cloudera、Hortonworks。 Apache版本最原始&#xff08;最基础&#xff09;的版本&#xff0c…

【三十天精通Vue 3】第二十六天 Vue3 与 TypeScript 最佳实践

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: 三十天精通 Vue 3 文章目录 引言一、为什么使用TypeScript&#xff1f;二、Vue 3和TypeScript…

Golang每日一练(leetDay0055)

目录 159.至多包含两个不同字符的最长子串 Longest-substring-with-at-most-two-distinct-characters &#x1f31f;&#x1f31f; 160. 相交链表 Intersection-of-two-linked-lists &#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 …

Stable Diffusion +ChatGPT+文本转语音+剪映制作视频

目录 chatgpt免费体验入口网址 模型下载 huggingface.co civitai.com 使用Deliberate模型案例 StableDeffusion做的图片&#xff0c;chatGPT出的文案&#xff0c;微软文本转语音配的音&#xff0c;使用剪映做的视频 chatgpt免费体验入口网址 http://chat.xutongbao.top …

【Java数据结构】顺序表、队列、栈、链表、哈希表

顺序表 定义 存放数据使用数组但是可以编写一些额外的操作来强化为线性表&#xff0c;底层依然采用顺序存储实现的线性表&#xff0c;称为顺序表 代码实现 创建类型 先定义一个新的类型 public class ArrayList<E> {int capacity 10; //顺序表的最大容量int size …

UNIX环境高级编程——信号

10.1 引言 信号是软件中断&#xff1b;信号提供了一种处理异步事件的方法。 10.2 信号概念 每个信号都有一个名字&#xff0c;这些名字都以3个字符SIG开头&#xff1b;在头文件<signal.h>中&#xff0c;信号名都被定义为正整数常量&#xff08;信号编号&#xff09;&a…

架构设计-高性能篇

大家好&#xff0c;我是易安&#xff01;今天我们谈一谈架构设计中的高性能架构涉及到的底层思想。本文分为缓存架构&#xff0c;单服务器高性能模型&#xff0c;集群下的高性能模型三个部分&#xff0c;内容很干&#xff0c;希望你仔细阅读。 高性能缓存架构 在某些复杂的业务…