Dubbo
官方文档:
Java SDK 手册 | Apache Dubbo
一 RPC及Dubbo
1 什么是RPC
dubbo是⼀款⾼性能的rpc框架。什么是rpc呢?
rpc是⼀种协议:是⼀种远程过程调⽤(remote procudure call)协议
rpc协议是在应⽤层之上的协议,规定了通信双⽅进⾏通信的数据格式是什么样的,及数据如何传输:
指明调⽤的类或接口
指明调⽤的⽅法及参数
两个服务之间通信,需要指定好消息的传输格式,像http请求,他规定了信息传输要指定请求头,请求行,请求体等。这就是rpc需要做的事
2 什么是dubbo
Apache Dubbo 是⼀款⾼性能、轻量级的开源服务框架。Apache Dubbo |ˈdʌbəʊ| 提供了六⼤核⼼能力:
⾯向接⼝代理的⾼性能RPC调⽤,
智能容错
和负载均衡,
服务⾃动注册和发现,
⾼度可扩展能⼒,
运⾏期流量调度,
可视化的服务治理与运维。
我们主要学rpc和lb
3 dubbo怎么实现远程通信?
这个图展示了一个基于Dubbo框架的服务调用流程,Dubbo是一个高性能的Java RPC(远程过程调用)框架。下面是根据图中的流程描述的Dubbo工作流程:
1. **服务提供者(服务提供者)**:
- 服务提供者需要实现服务接口,并注册到注册中心。
- 服务提供者启动时,会暴露服务(启动Tomcat、Jetty等服务器),并将其服务接口、实现类、服务地址及端口等注册信息发送到注册中心。
2. **注册中心(注册中心)**:
- 注册中心负责保存服务名与服务地址的映射关系。
- 当服务提供者注册服务后,注册中心会存储这些信息,并在服务提供者地址变更时主动通知消费者。
3. **服务消费者(服务消费者)**:
- 服务消费者在启动时,会从注册中心获取服务地址列表。
- 服务消费者将这些地址信息缓存在本地,以便进行远程调用。
- 服务消费者通过负载均衡策略选择一个服务提供者进行调用。
4. **远程调用**:
- 服务消费者使用指定的参数(接口名、方法名、方法参数类型列表、参数值列表)进行远程调用。
- 这个调用过程对服务消费者来说是透明的,就像调用本地方法一样。
5. **监控中心(监控中心)**:
- 监控中心负责对服务的监控及治理。
- 服务提供者和消费者都会向监控中心发送监控数据,监控中心可以对这些数据进行分析,以便于服务的维护和管理。
整个流程中,注册中心起到了服务发现的作用,使得服务消费者能够动态地发现服务提供者。服务消费者通过负载均衡策略选择服务提供者,可以提高系统的可用性和扩展性。监控中心则提供了服务的监控和治理功能,帮助维护服务的稳定性。
4 具体代码实现远程通信
从上面图的逻辑,远程通信主要有三部分组成:服务消费者,服务提供者,服务本身。
服务
简单地模拟了一个服务,创建了一个结构和他的实现类。里面接受一个字符串参数a,然后返回一个 “hello” + a 的字符串
public interface HelloService {String hello(String name);
}public class HelloServiceImpl implements HelloService {@Overridepublic String hello(String name) {return "hello:" + name;}
}
服务提供者
服务提供者实现了:
1 服务提供者需要实现服务接口,并注册到注册中心。
2 服务提供者启动时,会暴露服务(启动Tomcat、Jetty等服务器)
3 并将其服务接口、实现类、服务地址及端口等注册信息发送到注册中心。
我们下面逐一分析
public class Provider {public static void main(String[] args) {//封装服务提供者地址URL url = new URL("localhost",8080);//模拟远程注册中心//将服务和地址关联起来RemoteMapRegister.regist(HelloService.class.getName(), url);//指明服务的实现类LocalRegister.regist(HelloService.class.getName(), HelloServiceImpl.class);//指定远程调用协议, 这里是http请求Protocol protocol = ProtocolFactory.getProtocol();// 开启接收请求protocol.start(url);}
}
需求1 服务提供者需要实现服务接口,并注册到注册中心 并将注册信息发送到注册中心
首先提供者封装了服务的url,然后,将服务和地址注册到注册中心
//封装服务提供者地址URL url = new URL("localhost",8080);//模拟远程注册中心//将服务和地址关联起来RemoteMapRegister.regist(HelloService.class.getName(), url);
服务中心的具体代码,由于我们为了讲解功能的实现,就不使用框架,也就是先不使用zookepper来实现注册中心
这里面的map----REGISTER,是储存服务名字和服务地址的映射。
还有注册方法regist,就是将服务名字和服务地址映射起来
得到注册信息get,得到将服务名字和服务地址的映射
注册到文件中,实现持久化saveFile
得到注册信息getFile
public class RemoteMapRegister {private static Map<String, List<URL>> REGISTER = new HashMap<>();public static void regist(String interfaceName, URL url){List<URL> list = REGISTER.get(interfaceName);if (list == null) {list = new ArrayList<>();}list.add(url);REGISTER.put(interfaceName, list);//saveFile实现REGISTER在provider和consumer之间共享saveFile();}public static List<URL> get(String interfaceName) {REGISTER = getFile();List<URL> list = REGISTER.get(interfaceName);return list;}private static void saveFile() {try {FileOutputStream fileOutputStream = new FileOutputStream("classpath:register.txt");ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);objectOutputStream.writeObject(REGISTER);} catch (IOException e) {e.printStackTrace();}}private static Map<String, List<URL>> getFile() {try {FileInputStream fileInputStream = new FileInputStream("classpath:register.txt");ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);return (Map<String, List<URL>>) objectInputStream.readObject();} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}return null;}
}
需求2 创建服务接口、实现类的关联
//指明服务的实现类LocalRegister.regist(HelloService.class.getName(), HelloServiceImpl.class);
创建一个类封装服务接口、实现类的关联。
有存储关联的Map-----map
有创建关联的regist
有得到关联的get
public class LocalRegister {private static Map<String, Class> map = new HashMap<>();public static void regist(String interfaceName, Class implClass) {map.put(interfaceName, implClass);}public static Class get(String interfaceName) {return map.get(interfaceName);}
}
需求3 服务提供者启动时,会暴露服务(启动Tomcat、Jetty等服务器)
//指定远程调用协议, 这里是http请求Protocol protocol = ProtocolFactory.getProtocol();// 开启接收请求protocol.start(url);
首先我们会先设置请求是什么协议的
会创建一个类来提取协议名字然后创建对应的协议类。(都是自己封装的)
假如要http协议
public class ProtocolFactory {public static Protocol getProtocol() {//vm options:-DprotocolName=dubboString name = System.getProperty("protocolName");if (name == null || name.equals("")) name = "http";switch (name) {case "http":return new HttpProtocol();case "dubbo":return new DubboProtocol();default:break;}return new HttpProtocol();}
}
调用start方法,最终会start封装好的Tomcat,由于tomcat内配置了DispatcherServlet,他会拦截一切请求。也就是将服务暴露了之后交由DispatcherServlet处理相应逻辑
public class HttpServer {public void start(String hostname, Integer port) {Tomcat tomcat = new Tomcat();Server server = tomcat.getServer();Service service = server.findService("Tomcat");Connector connector = new Connector();connector.setPort(port);Engine engine = new StandardEngine();engine.setDefaultHost(hostname);Host host = new StandardHost();host.setName(hostname);String contextPath = "";Context context = new StandardContext();context.setPath(contextPath);context.addLifecycleListener(new Tomcat.FixContextListener());host.addChild(context);engine.addChild(host);service.setContainer(engine);service.addConnector(connector);tomcat.addServlet(contextPath, "dispatcher", new DispatcherServlet());context.addServletMappingDecoded("/*", "dispatcher");try {tomcat.start();tomcat.getServer().await();} catch (LifecycleException e) {e.printStackTrace();}}
}
DispatcherServlet拦截请求,并通过传进来的参数用反射调用服务的方法,最终实现远程调用服务
public class DispatcherServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {new HttpServerHandler().handler(req, resp);}
}public class HttpServerHandler {public void handler(HttpServletRequest req, HttpServletResponse resp) {try {ObjectInputStream ois = new ObjectInputStream(req.getInputStream());Invocation invocation = (Invocation)ois.readObject();String interfaceName = invocation.getInterfaceName();//HelloServiceClass implClass = LocalRegister.get(interfaceName);//HelloServiceImpl.hello(String name)Method method = implClass.getMethod(invocation.getMethodName(), invocation.getParamTypes());String result = (String) method.invoke(implClass.newInstance(), invocation.getParams());//HelloServiceImpl.hello(String name)System.out.println("tomcat:" + result);IOUtils.write(result.getBytes(), resp.getOutputStream());//写回给服务的消费者} catch (Exception e) {e.printStackTrace();}}
}
服务消费者
主要代码:
通过代理实现相应逻辑
public class Consumer {public static void main(String[] args) {HelloService helloService = ProxyFactory.getProxy(HelloService.class);String name = helloService.hello("qf");System.out.println(name);}
}
需求1 : 服务消费者在启动时,会从注册中心获取服务地址列表。服务消费者将这些地址信息缓存在本地,以便进行远程调用 服务消费者通过负载均衡策略选择一个服务提供者进行调用。
首先会得到服务名字,服务方法名字,参数。
然后通过服务名字在注册中心得到请求的url
之后用过负载均衡实现请求
public class ProxyFactory<T> {@SuppressWarnings("unchecked")public static <T> T getProxy(final Class interfaceClass) {return (T)Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//Invocation封装的是这一次远程过程调用所需要的所有的参数Invocation invocation = new Invocation(interfaceClass.getName(),method.getName(), method.getParameterTypes(), args);//去注册中心获得服务地址列表List<URL> urlList = RemoteMapRegister.get(interfaceClass.getName());//负载均衡URL url = LoadBalance.random(urlList);Protocol protocol = ProtocolFactory.getProtocol();String result = protocol.send(url, invocation);return result;}});}
}
需求2: 服务消费者使用指定的参数(接口名、方法名、方法参数类型列表、参数值列表)进行远程调用 这个调用过程对服务消费者来说是透明的,就像调用本地方法一样。
封装url和参数调用send方法,send方法通过io流,得到结果并返回
//调用send方法
String result = protocol.send(url, invocation);//send方法的实现
public class HttpClient {public String send(String hostname, Integer port, Invocation invocation) {try {URL url = new URL("http", hostname, port, "/");HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();httpURLConnection.setRequestMethod("POST");httpURLConnection.setDoOutput(true);OutputStream outputStream = httpURLConnection.getOutputStream();ObjectOutputStream oos = new ObjectOutputStream(outputStream);oos.writeObject(invocation);oos.flush();oos.close();InputStream inputStream = httpURLConnection.getInputStream();String result = IOUtils.toString(inputStream);return result;} catch (Exception e) {e.printStackTrace();}return null;}
}
5 dubbo内部结构
二 快速入门
使用springboot整合dubbo
1 服务
在父工程下创建一个maven,下面定义一个接口,作为服务
2 服务提供者
2.1 引入依赖
我们用的是jdk1.8 boot版本2.3.7
以及引入了dubbo-spring-boot-starter, dubbo-registry-zookeeper
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.qf</groupId><artifactId>my-boot-service-provider</artifactId><version>0.0.1-SNAPSHOT</version><name>my-boot-service-provider</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.3.7.RELEASE</spring-boot.version></properties><dependencies><dependency><groupId>com.qf</groupId><artifactId>my-boot-qf-interfaces</artifactId><version>1.0-SNAPSHOT</version></dependency><!-- Dubbo Spring Boot Starter --><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>2.7.3</version></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-registry-zookeeper</artifactId><version>2.7.3</version><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.3.7.RELEASE</version><configuration><mainClass>com.qf.my.boot.service.provider.MyBootServiceProviderApplication</mainClass></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>
2.2 编写配置文件
server:port: 9001
dubbo:application:name: site-service-boot-providerregistry:address: zookeeper://ip:2181protocol:name: dubboport: 20882
2.3 开启@EnableDubbo注解
在启动类上添加,也可以自己编写配置类添加
@EnableDubbo
@SpringBootApplication
public class MyBootServiceProviderApplication {public static void main(String[] args) {SpringApplication.run(MyBootServiceProviderApplication.class, args);}}
2.4 提供服务实现类
注意这里的@Service是dubbo的,不是spring的,在2.7.7dubbo版本之后会改成@DubboService
来区分。我这里请注意版本
import com.qf.boot.api.SiteService;
import org.apache.dubbo.config.annotation.Service;/*** @author Thor* @公众号 Java架构栈*/
@Service
public class SiteServiceImpl implements SiteService {@Overridepublic String getName(String name) {return "hello boot:" + name;}
}
3 服务消费者
3.1 引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.qf</groupId><artifactId>my-boot-service-consumer</artifactId><version>0.0.1-SNAPSHOT</version><name>my-boot-service-consumer</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.3.7.RELEASE</spring-boot.version></properties><dependencies><dependency><groupId>com.qf</groupId><artifactId>my-boot-qf-interfaces</artifactId><version>1.0-SNAPSHOT</version></dependency><!-- Dubbo Spring Boot Starter --><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>2.7.3</version></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-registry-zookeeper</artifactId><version>2.7.3</version><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.3.7.RELEASE</version><configuration><mainClass>com.qf.my.boot.service.consumer.MyBootServiceConsumerApplication</mainClass></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>
3.2 配置文件
server:port: 8001
dubbo:application:name: site-service-boot-consumer# 监听,订阅的地址registry:address: zookeeper://ip:2181
3.3 消费逻辑
编写一个controller
注意!!!
这里的@Reference也是dubbo包下的
@RestController
@RequestMapping("/site")
public class SiteController {//<dubbo:reference : 两件事情:1.服务启动时向注册中心订阅SiteService服务地址列表 2.生成SiteService服务的代理对象@Referenceprivate SiteService siteService;@GetMapping("/name")public String getName(String name){return siteService.getName(name);}}
3.4 加上@EnableDubbo注解
在启动类上
@EnableDubbo
@SpringBootApplication
public class MyBootServiceConsumerApplication {public static void main(String[] args) {SpringApplication.run(MyBootServiceConsumerApplication.class, args);}}
4 测试启动
成功
三 dubbo⽤法示例
0 先决条件
服务
首先要有服务,这里定义了一些类,具体逻辑不关心,重点在实现类
服务提供者
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.qf</groupId><artifactId>my-dubbo-provider</artifactId><version>0.0.1-SNAPSHOT</version><name>my-dubbo-provider</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.3.7.RELEASE</spring-boot.version></properties><dependencies><!-- Dubbo Spring Boot Starter --><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>2.7.3</version></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-registry-zookeeper</artifactId><version>2.7.3</version><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-rpc-http</artifactId><version>2.7.3</version></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-metadata-report-zookeeper</artifactId><version>2.7.3</version></dependency><!-- Dubbo rest protocol --><dependency><groupId>org.jboss.resteasy</groupId><artifactId>resteasy-jaxrs</artifactId><version>3.0.19.Final</version></dependency><dependency><groupId>javax.validation</groupId><artifactId>validation-api</artifactId><version>1.1.0.Final</version></dependency><dependency><groupId>com.qf.dubbo</groupId><artifactId>my-dubbo-interfaces</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.3.7.RELEASE</version><configuration><mainClass>com.qf.my.dubbo.provider.MyDubboProviderApplication</mainClass></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>
配置文件
注意 dubbo.scan.base-packages=com.qf.my.dubbo.provider
spring.application.name=my-dubbo-providerserver.port=8080# Base packages to scan Dubbo Component: @org.apache.dubbo.config.annotation.Service==>@EnableDubbo
# 我们没有配置@EnableDubbo,@EnableDubbo主要用来扫描服务和扫描配置文件,下面就用来扫描文件
dubbo.scan.base-packages=com.qf.my.dubbo.provider
dubbo.application.name=${spring.application.name}## Dubbo Registry
dubbo.registry.address=zookeeper://ip:2181# Dubbo Protocol
#dubbo.protocol.name=dubbo
#dubbo.protocol.port=20880# @Path
#dubbo.protocol.name=rest
#dubbo.protocol.port=8083dubbo.protocols.protocol1.id=dubbo1
dubbo.protocols.protocol1.name=dubbo
dubbo.protocols.protocol1.port=20881
dubbo.protocols.protocol1.host=0.0.0.0dubbo.protocols.protocol2.id=dubbo2
dubbo.protocols.protocol2.name=dubbo
dubbo.protocols.protocol2.port=20882
dubbo.protocols.protocol2.host=0.0.0.0dubbo.protocols.protocol3.id=dubbo3
dubbo.protocols.protocol3.name=dubbo
dubbo.protocols.protocol3.port=20883
dubbo.protocols.protocol3.host=0.0.0.0
服务消费者
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.qf</groupId><artifactId>my-dubbo-consumer</artifactId><version>0.0.1-SNAPSHOT</version><name>my-dubbo-consumer</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.3.7.RELEASE</spring-boot.version></properties><dependencies><dependency><groupId>com.qf.dubbo</groupId><artifactId>my-dubbo-interfaces</artifactId><version>1.0-SNAPSHOT</version></dependency><!-- Dubbo Spring Boot Starter --><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>2.7.3</version></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-registry-zookeeper</artifactId><version>2.7.3</version><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.3.7.RELEASE</version><configuration><mainClass>com.qf.my.dubbo.consumer.MyDubboConsumerApplication</mainClass></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>
配置文件
spring.application.name=my-dubbo-consumerserver.port=8009
# registry
dubbo.registry.address= zookeeper://ip:2181
1 version版本号
版本号用处是对于同⼀个接⼝,具有不同的服务实现。
在@Service中加上参数 version 即 @Service(version = "xxx")
服务提供者
版本号为default的
@Service(version = "default")
public class DefaultSiteServiceImpl implements SiteService {@Overridepublic String siteName(String name) {return "default:"+name;}
}
版本号为async的。
@Service(version = "async")
public class AsyncSiteServiceImpl implements SiteService {@Overridepublic String siteName(String name) {return "async:" + name;}
}
服务消费者
由于我们没有使用启动类,没有加@EnableDubbo注解,所以用@EnableAutoConfiguration来读取配置
我们可以在@Reference中加上参数version 指定具体实现哪一个实现类
即 @Reference(version = "xxx")
@EnableAutoConfiguration
public class DefaultDubboConsumer {// @Reference(version = "async")@Reference(version = "default")private SiteService siteService;public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(DefaultDubboConsumer.class);SiteService siteService = (SiteService) context.getBean(SiteService.class);String name = siteService.siteName("q-face");System.out.println(name);}
}
2 指定protocol协议
服务提供者
配置文件
我们在配置文件中配置了3个协议,可以使用dubbo,rest协议,自己配就好
spring.application.name=my-dubbo-providerserver.port=8080# Base packages to scan Dubbo Component: @org.apache.dubbo.config.annotation.Service==>@EnableDubbo
dubbo.scan.base-packages=com.qf.my.dubbo.provider
dubbo.application.name=${spring.application.name}## Dubbo Registry
dubbo.registry.address=zookeeper://192.168.6.135:2181dubbo.protocols.protocol1.id=dubbo1
dubbo.protocols.protocol1.name=dubbo
dubbo.protocols.protocol1.port=20881
dubbo.protocols.protocol1.host=0.0.0.0dubbo.protocols.protocol2.id=dubbo2
dubbo.protocols.protocol2.name=dubbo
dubbo.protocols.protocol2.port=20882
dubbo.protocols.protocol2.host=0.0.0.0dubbo.protocols.protocol3.id=dubbo3
dubbo.protocols.protocol3.name=dubbo
dubbo.protocols.protocol3.port=20883
dubbo.protocols.protocol3.host=0.0.0.0
服务提供
@Service中添加参数 protocol 为 protocol1,指定我们的协议
@Service(version = "default", protocol = "protocol1")
public class DefaultSiteServiceImpl implements SiteService {@Overridepublic String siteName(String name) {return "default:"+name;}
}
服务消费者
@EnableAutoConfiguration
public class DefaultDubboConsumer {@Reference(version = "default")private SiteService siteService;public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(DefaultDubboConsumer.class);SiteService siteService = (SiteService) context.getBean(SiteService.class);String name = siteService.siteName("q-face");System.out.println(name);}
}
特别的
当我们为提供者配置多个协议(注意不是多种!),并且服务提供者不指定特定的协议
即:
@Service(version = "default")
public class DefaultSiteServiceImpl implements SiteService {@Overridepublic String siteName(String name) {return "default:"+name;}
}
那么会为每一个协议提供一个服务,上面我们配置了三个协议,那么就是三个服务
3 使⽤rest访问dubbo的服务
服务提供者
配置文件
其中,我们配置了一个rest协议。
rest协议,可以简单地将他理解成RestFul风格,可以在下面网址中了解
REST详解_rest协议-CSDN博客
spring.application.name=my-dubbo-providerserver.port=8080# Base packages to scan Dubbo Component: @org.apache.dubbo.config.annotation.Service==>@EnableDubbo
dubbo.scan.base-packages=com.qf.my.dubbo.provider
dubbo.application.name=${spring.application.name}## Dubbo Registry
dubbo.registry.address=zookeeper://192.168.6.135:2181dubbo.protocols.protocol1.id=rest1
dubbo.protocols.protocol1.name=rest
dubbo.protocols.protocol1.port=8083
dubbo.protocols.protocol1.host=0.0.0.0dubbo.protocols.protocol2.id=dubbo2
dubbo.protocols.protocol2.name=dubbo
dubbo.protocols.protocol2.port=20882
dubbo.protocols.protocol2.host=0.0.0.0dubbo.protocols.protocol3.id=dubbo3
dubbo.protocols.protocol3.name=dubbo
dubbo.protocols.protocol3.port=20883
dubbo.protocols.protocol3.host=0.0.0.0
服务实现
注意里面使用的注解,path,get都体现着rest风格
import com.qf.site.SiteService;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.rpc.protocol.rest.support.ContentType;import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;/*** @author Thor* @公众号 Java架构栈*/
@Service(version = "rest", protocol = "protocol1")
@Path("site")
public class RestSiteService implements SiteService {@Override@GET@Path("name")@Produces({ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_PLAIN_UTF_8})public String siteName(@QueryParam("name") String name) {return "rest:" + name;}
}
服务消费者
我们可以直接请求路径
成功
4 服务超时
服务提供者
@Service(version = "timeout", timeout = 4000)
public class TimeoutSiteServiceImpl implements SiteService {@Overridepublic String siteName(String name) {try {//如果服务执行时间超过了指定的超时时间则会打印warn日志Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("serving...");return "timeout site service:"+name;}
}
警告日志
服务消费者
设置了3秒的超时时间
@EnableAutoConfiguration
public class TimeoutDubboConsumer {@Reference(version = "timeout", timeout = 3000)private SiteService siteService;public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(TimeoutDubboConsumer.class);SiteService siteService = (SiteService) context.getBean(SiteService.class);String name = siteService.siteName("q-face");System.out.println(name);}
}
报错异常:
5 集群容错
dubbo为集群调⽤提供了容错⽅案:
failover:(默认,推荐)
当出现失败时,会进⾏重试,默认重试2次,⼀共三次调⽤。但是会出现幂等性问题。
虽然会出现幂等性问题,但是依然推荐使⽤这种容错机制,在业务层⾯解决幂等性问题:
- ⽅案⼀:把数据的业务id作为数据库的联合主键,此时业务id不能重复。
- ⽅案⼆(推荐):使⽤分布式锁来解决重复消费问题。
failfast:当出现失败时。⽴即报错,不进⾏重试。
failsafe:失败不报错,记⼊⽇志。
failback:失败就失败,开启定时任务 定时重发。
forking:并⾏访问多个服务器,获取某⼀个结果既视为成功。
结论:如果使⽤dubbo,不推荐把重试关掉,⽽是在⾮幂等性操作的场景下,服务提供者⽅
要做幂等性的解决⽅案(保证)。
6 服务降级
服务消费者通过Mock指定服务超时后执⾏的策略:
@EnableAutoConfiguration
public class MockDubboConsumer {
// @Reference(version = "timeout", timeout = 1000, mock = "fail:return timeout")@Reference(version = "timeout", timeout = 1000, mock = "force:return hello")private SiteService siteService;public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(MockDubboConsumer.class);SiteService siteService = (SiteService) context.getBean(SiteService.class);String name = siteService.siteName("q-face");System.out.println(name);}
}
7 本地存根
客户端
在 @Reference 上添加超时1秒,并且添加 stub 参数设置成 true,开启本地存根
import com.qf.site.SiteService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;/*** @author Thor* @公众号 Java架构栈*/
@EnableAutoConfiguration
public class StubDubboConsumer {@Reference(version = "timeout", timeout = 1000, stub = "true")private SiteService siteService;public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(StubDubboConsumer.class);SiteService siteService = (SiteService) context.getBean(SiteService.class);String name = siteService.siteName("q-face");System.out.println(name);}}
这个时候,我们可以定义一个类,名字是:在 SiteService 这个类后拼接stub -> SiteServicestub 然后在Service同一包下去创建他 ,这里的例子是在 com.qf.site包下创建,和我们服务的接口一致。由于这个intefaces在消费者微服务的pom中被引入了,所以实际上是消费者在调用这个api
我们就可以在这个类中来定义我们客户端额外需要执行的逻辑
stub
如果服务提供者报错了,消费者就会调用这个stub来实行catch的逻辑
public class SiteServiceStub implements SiteService {private final SiteService siteService;public SiteServiceStub(SiteService siteService) {this.siteService = siteService;}@Overridepublic String siteName(String name) {try {return siteService.siteName(name);} catch (Exception e) {return "stub:"+name;}}
}
8 参数回调
接口层
public interface SiteService {//同步调用方法String siteName(String name);//回调方法default String siteName(String name, String key, SiteServiceListener siteServiceListener){return null;}//异步调用方法default CompletableFuture<String> siteNameAsync(String name){return null;}
}
服务提供者
method参数,指定我们回调的方法是 name = "siteName",
arguments = {@Argument(index = 2, callback = true)})},代表我们用来调用回调方法的方法中,在第三个参数就是回调方法。也就是 SiteServiceListener
callbacks = 3 服务端最多可以为一个请求提供 3 次回调
@Service(version = "callback", methods = {@Method(name = "siteName", arguments = {@Argument(index = 2, callback = true)})}, callbacks = 3)
public class CallbackSiteServiceImpl implements SiteService {@Overridepublic String siteName(String name) {return null;}@Overridepublic String siteName(String name, String key, SiteServiceListener siteServiceListener) {siteServiceListener.changed("provider data");return "callback:"+name;}
}
创建回调方法
public interface SiteServiceListener {void changed(String data);
}public class SiteServiceListenerImpl implements SiteServiceListener, Serializable {@Overridepublic void changed(String data) {System.out.println("changed:" + data);}
}
消费者
@EnableAutoConfiguration
public class CallbackDubboConsumer {@Reference(version = "callback")private SiteService siteService;public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(CallbackDubboConsumer.class);SiteService siteService = (SiteService) context.getBean(SiteService.class);// key 目的是指明实现类在服务提供者和消费者之间保证是同一个System.out.println(siteService.siteName("q-face", "c1", new SiteServiceListenerImpl()));System.out.println(siteService.siteName("q-face", "c2", new SiteServiceListenerImpl()));System.out.println(siteService.siteName("q-face", "c3", new SiteServiceListenerImpl()));}
}
四 负债均衡
相关文章:
Dubbo
官方文档: Java SDK 手册 | Apache Dubbo 一 RPC及Dubbo 1 什么是RPC dubbo是⼀款⾼性能的rpc框架。什么是rpc呢? rpc是⼀种协议:是⼀种远程过程调⽤(remote procudure call)协议 rpc协议是在应⽤层之上的协议&…...
算法1(蓝桥杯18)-删除链表的倒数第 N 个节点
问题: 给你一个链表,删除链表的倒数第 n 个节点,并且返回链表的头节点。 输入:head 1 -> 2 -> 3 -> 4 -> 5 -> null, n 2 输出:1 -> 2 -> 3 -> 5 -> null输入:head 1 ->…...
SEC_ASA 第一天作业
拓扑: 实验需求: 注意:在开始作业之前必须先读“前言”,以免踩坑!!!(☞敢点我试试) 按照拓扑图配置VLAN连接。 注意:ASA防火墙的 Gi0/1口需要起子接口&#x…...
《C语言程序设计现代方法》note-8 指针和数组的关系
文章目录 助记提要12章 指针和数组12.1 指针的算术运算12.2 指针用于数组处理结合使用*和运算符 12.3 数组名作为指针数组名可以用作指针指针也可以当做数组名数组型实参 12.4 指针和多维数组处理每个元素处理行处理列多维数组名做指针 12.5 指针和变长数组 助记提要 指针支持…...
安科瑞电能质量治理产品在分布式光伏电站的应用-安科瑞黄安南
1.概述 随着全球对可再生能源需求的增加,分布式光伏电站的建设和发展迅速。然而,分布式光伏电站的运行过程中面临着一系列问题,比如导致企业关口计量点功率因数过低、谐波污染等。这些问题不仅影响光伏电站自身的运行效率,还会对…...
JavaScript 的原生数组方法和 Vue 的响应式系统之间的差异
发现宝藏 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【宝藏入口】。 JavaScript 的原生数组方法和 Vue 的响应式系统之间的差异主要体现在 Vue 如何追踪数组的变化,以及 Vue 如何处理数组…...
项目组件框架介绍[bRPC]
文章目录 前言bRPC安装bRPC的简单使用protobuf简单使用Echo服务远程调用Echo服务 与etcd关联 前言 bRPC是百度开源的一款工业级RPC框架,功能强大, 常用于搜索、存储、机器学习、广告、推荐等高性能系统。 bRPC安装 使用源码安装即可, 在安装前要确认依赖 sudo apt…...
基于MobileNetV3架构动物声音分类识别与应用系统实现
1.摘要 本文主要实现了一个基于MobileNetV3架构的深度学习模型用于动物声音分类识别任务。该MobileNetV3是一种轻量级的卷积神经网络,旨在实现高效分类性能,本章在猫、狗、海豚等三个动物声音(.wav数据集)上进行了训练和测试,即在…...
ragflow连ollama时出现的Bug
ragflow和ollama连接后,已经添加了两个模型但是ragflow仍然一直warn:Please add both embedding model and LLM in Settings > Model providers firstly.这里可能是我一开始拉取的镜像容器太小,容不下当前添加的模型,导…...
[大数据]Hudi编译集成
1. 编译环境准备 相关组件版本如下: Hadoop3.3.1Hive3.1.3Flink1.13.6,scala-2.12Spark3.3.1,scala-2.12 1)安装Maven (1)上传apache-maven-3.6.1-bin.tar.gz到/opt/software目录,并解压更名…...
rk3588-ubuntu22.04系统网关实现路由器功能:
rk3588-ubuntu22.04系统网关实现路由器功能: 场景需求描述: 需求背景: 场景一:通过网线eth0/(路由器wlan0)访问外网: 如果网关 和 设备所处的环境可以通过网线联网或者路由器联网,那么不需要将网关配置成…...
Python部署教程-Python项目怎样在Pycharm中运行
大家好,我是程序员徐师兄,今天为大家带来的是Python部署教程-Python项目怎样在Pycharm中运行。Python安装部署教程,包括软件的下载,软件的安装。该系统采用 Python语言开发,flask、Django 框架,MySql 作为数…...
代码随想录算法训练营第51期第14天 | 226. 翻转二叉树、101. 对称二叉树、104.二叉树的最大深度、111.二叉树的最小深度
226. 翻转二叉树 226. 翻转二叉树https://leetcode.cn/problems/invert-binary-tree/1.昨天忘了声明,如果都用C的话,我大概率写不完,所以思路方面,我可能考虑用pyhon先写,后续会用文心一言转换成C 2.这里可以直接用层…...
C/C++常见符号与运算符
C/C常见符号与运算符对照表 符号用法与意义与Java类比:在条件运算符中 (cond ? x : y) 表示条件为假的分支;在 switch-case 中如 case 1:表示标签结束点;在自定义标签如 label: 中用于 goto 跳转Java中? :三元运算相同;switch-case中也有:…...
maven报错“找不到符号“
问题 springboot项目 maven编译打包过程,报错"找不到符号" 解决 很多网上方法都试过,都没用 换jdk,把17->21...
开源数据同步中间件(Dbsyncer)简单玩一下 mysql to mysql 的增量,全量配置
一、什么是Dbsyncer 1、介绍 Dbsyncer是一款开源的数据同步中间件,提供MySQL、Oracle、SqlServer、PostgreSQL、Elasticsearch(ES)、Kafka、File、SQL等同步场景,支持上传插件自定义同步转换业务,提供监控全量和增量数据统计图、应用性能预警…...
【Kubernetes理论篇】容器集群管理系统Kubernetes(K8S)
Kubernetes集群部署基本管理实战 这么好的机会,还在等什么! 01、Kubernetes 概述 K8S是什么 K8S 的全称为 Kubernetes (K12345678S),PS:“嘛,写全称也太累了吧,写”。不如整个缩写 K8s 作为缩写的结果…...
鸿蒙ArkTS语言基础语法详解
文章目录 鸿蒙ArkTS语言基础语法详解一、引言二、ArkTS语言概述1. ArkTS语言特点2. TypeScript基础语法2.1 类型注解2.2 接口2.3 泛型2.4 类的继承2.5 类的访问修饰符 三、ArkTS的基本组成3.1 装饰器3.2 UI描述3.3 自定义组件3.4 系统组件3.5 属性方法和事件方法 四、自定义组件…...
两条链表相同位数相加
优质博文IT-BLOG-CN 一、题目 给你两个非空的链表,表示两个非负的整数。它们每位数字都是按照逆序的方式存储的,并且每个节点只能存储一位数字。请你将两个数相加,并以相同形式返回一个表示和的链表。你可以假设除了数字0之外,这…...
C语言单元总结
黑色加粗表示刷题刷到这样的题 红色加粗表示可能重要 单元一 程序设计宏观认识 C语言程序框架 C语言程序最基本的程序框架由两部分构成,分别是 1) 编译预处理 2) 函数组 C语言程序构成 C程序最大的特点就是所有的程序都是用函数来装配的,函数是构成…...
【Golang】Go语言编程思想(六):Channel,第二节,使用Channel等待Goroutine结束
使用 Channel 等待任务结束 首先回顾上一节 channel 这一概念介绍时所写的代码: package mainimport ("fmt""time" )func worker(id int, c chan int) {for n : range c {fmt.Printf("Worker %d received %c\n",id, n)} }func crea…...
Oracle RAC开启和关闭日志归档Log Archive
一、开启日志归档模式 # srvctl stop database -d <DB_NAME> # srvctl start instance -d <DB_NAME> -i <INSTANCE_NAME> -o mount # 停止 RAC 数据库的所有实例: [oracleora19crac1:/home/oracle]$srvctl stop database -d orcl # 启动第一个实…...
今天调了个转速的小BUG
同事说转速表有个bug,转速停止后,继电器没有恢复到初始状态。若停止之前是报警,继电器吸合,则停止后继电器还是吸合。我心想不会啊,这软件都弄了好几年了,一直也没出现过状况。 经过与调试同事的沟通&#…...
RabbitMQ七种工作模式之 RPC通信模式, 发布确认模式
文章目录 六. RPC(RPC通信模式)客户端服务端 七. Publisher Confirms(发布确认模式)1. Publishing Messages Individually(单独确认)2. Publishing Messages in Batches(批量确认)3. Handling Publisher Confirms Asynchronously(异步确认) 六. RPC(RPC通信模式) 客⼾端发送消息…...
【Python教程】Python3基础篇之基础语法
博主介绍:✌全网粉丝21W+,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物联网、机器学习等设计与开发。 感兴趣的可…...
`yarn list --pattern element-ui` 是一个 Yarn 命令,用于列出项目中符合指定模式(`element-ui`)的依赖包信息
文章目录 命令解析:功能说明:示例输出:使用场景: yarn list --pattern element-ui 是一个 Yarn 命令,用于列出项目中符合指定模式( element-ui)的依赖包信息。 命令解析: yarn list…...
使用docker-compose安装Milvus向量数据库及Attu可视化连接工具
首先确保系统已经安装上了docker 然后去https://github.com/docker/compose/releases/下载安装docker-compose 跟随自己下系统和服务器情况下载 上传到服务器 mv docker-compose-linux-aarch64 docker-compose chmod x docker-compose2.dockr-compose命令 docker-compose …...
IoTDB Allocate WAL Buffer Fail Because out of memory
问题及现象 时序数据库 IoTDB 集群报错: The write is rejected because the wal directory size has reached the threshold 53687091200 bytes. You may need to adjust the flush policy of the storage storageengine or the IoTConsensus synchronization pa…...
Go支付中台方案:多平台兼容与多项目对接
一、中台的概念 中台是一种企业级的架构模式,它处于前台应用和后台资源之间,将企业核心能力进行整合、封装,形成一系列可复用的业务能力组件。这些组件就像乐高积木一样,可以被不同的前台业务快速调用,从而避免重复开…...
设计模式:23、享元模式
目录 0、定义 1、享元模式的三种角色 2、享元模式的UML类图 3、示例代码 0、定义 运营共享技术有效地支持大量细粒度的对象 1、享元模式的三种角色 享元接口(Flyweight):是一个接口,该接口定义了享元对外公开其内部数据的方…...
如何使用靜態IP代理?【詳細教程】
靜態IP地址是手動分配給設備或伺服器的固定不變的 IP。與動態 IP 地址不同,動態 IP 地址由 DHCP 伺服器自動分配,並且會隨時間而變化。 如何獲取和設置靜態IP地址 一、檢查是否需要靜態IP? 在配置靜態 IP 之前,請…...
C++day3
1、把课上类的三个练习题的构造函数写出来 #include <iostream> #include <cstring>using namespace std;class Car {string color;string brond;double speed; public:Car(string c,string b,double s):color("black"),brond("Benz"),speed(…...
ABAP开发-批量导入BAPI和BDC_1
系列文章目录 文章目录 系列文章目录[TOC](文章目录) 前言一、概念二、BDC和BAPI数据导入1、BDC数据导入(录屏)2、BAPI数据导入 三、实例1、BAPI2、BDC 总结 前言 一、概念 SAP中,对一个事务码反复操作并且达到批量处理数据的效果࿰…...
iOS 语音循环播放设置
本地文件、网络文件,区别就是URL创建方式 1、使用AVPlayerItem与AVPlayer(这个简单,只需要设置回调),而不是AVAudioPlayer(这个麻烦,需要设置代理、计时器等等) 2、设置AVPlayerIte…...
PlantUML——类图
背景 类图是UML模型中的静态视图,其主要作用包括: 描述系统的结构化设计,显示出类、接口以及它们之间的静态结构和关系。简化对系统的理解,是系统分析与设计阶段的重要产物,也是系统编码和测试的重要模型依据。 在U…...
ASP.NET Core实现鉴权授权的几个库
System.IdentityModel.Tokens.Jwt 和 Microsoft.AspNetCore.Authentication.JwtBearer 是两个常用的库,分别用于处理 JWT(JSON Web Token)相关的任务。它们在功能上有一定重叠,但侧重点和使用场景有所不同。 1. System.IdentityM…...
生成:安卓证书uniapp
地址: https://ask.dcloud.net.cn/article/35777 // 使用keytool -genkey命令生成证书: 官网: keytool -genkey -alias testalias -keyalg RSA -keysize 2048 -validity 36500 -keystore test.keystore ----------------------------------…...
探索Web3:从去中心化应用到全球数字化未来
Web3 是互联网发展的下一步,它通过去中心化的理念重新定义了数字世界。与传统的Web2相比,Web3将数据主权交还给用户,让每个人都可以在没有中介的情况下安全地交换信息和价值。本文将探索Web3的基本概念,去中心化应用(D…...
AR向左,AI向右,智能眼镜来到十字路口
从Google Glass到Vision Pro,人类对智能眼镜的探索有进展,但都不算成功,直至Ray-Ban Meta的出现,这才让行业对智能眼镜重燃信心,从去年开始,随着AI大模型应用的深入,智能眼镜又有了新的故事可说…...
CTF靶场搭建及Web赛题制作与终端docker环境部署
写在前面 ╔══════════════════════════════════════════════════════════════════════════╗ 哈喽大家好!我是Myon,趁着我的云服务器还没过期,这次给大家出一…...
Java 应用程序CPU 100%问题排查优化实战
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…...
计网(王道的总结)-数据链路层-网络层-传输层
由于时间有限,把每个王道的章节最后一节放在一起,分别看看复习知识点。 3.6.4 IEEE 802.11 无线局域网 重点: 3.7 广域网 真题考频:极低 3.8以太网交换机 4.1网络层的功能 4.2.1IPv4分组 最重要的: TTL:…...
1-4 C单向链表
目录 1.0 基本概念 2.0 初始化链表 2.0 插入数据 3.0 删除数据 4.0 获取链长度 5.0 查询链表 6.0 返回第一个节点 7.0 打印链表节点 8.0 释放内存 9.0 链表调用 1.0 基本概念 线性表的顺序存储:用一块连续的内存空间,线性表的链式存储ÿ…...
在Windows 10中使用SSH远程连接服务器(附花生壳操作方法)
SSH 在 linux 中是一种重要的系统组件,用户可以使用 SSH 来远程连接 linux 系统的计算机,或者传输文件。不过在 win10 以前,windows 并不原生支持 SSH,需要借助第三方工具来使用 SSH 功能。而实际上,微软在 2015 年就曾…...
韶音科技嵌入式面试题及参考答案
Bootloader 的启动流程是什么? Bootloader 是在操作系统内核运行之前运行的一段小程序。它的启动流程主要分为以下几个阶段。 首先是硬件初始化阶段。这个阶段会对处理器以及一些关键的硬件设备进行初始化。比如,会配置处理器的工作模式、设置堆栈指针等…...
C++ ——— 类的 6 个默认成员函数之 构造函数
目录 何为默认成员函数 一、构造函数 构造函数的概念 构造函数的特性 日期类的构造函数 栈的构造函数 编译器自动生成的构造函数 总结 何为默认成员函数 默认成员函数就是用户没有显示实现,但是编译器会自动生成的成员函数称为默认成员函数 一、构造函数 …...
【优选算法篇】:揭开二分查找算法的神秘面纱--数据海洋中的精准定位器
✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨ ✨ 个人主页:余辉zmh–CSDN博客 ✨ 文章所属专栏:c篇–CSDN博客 文章目录 一.二分查找算法二.算法模板模板一模板二模板三 三.例题演练1.x的平…...
【机器学习算法】——数据可视化
1. 饼图:显示基本比例关系 import matplotlib.pyplot as pltplt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False# ——————————————————————————————————————————————————————…...
比特币与区块链原理解析:矿机挖矿与去中心化的未来
✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢,在这里我会分享我的知识和经验。&am…...
SpringBoot教程(十四) SpringBoot之集成Redis
SpringBoot教程(十四) | SpringBoot之集成Redis 一、Redis集成简介二、集成步骤 2.1 添加依赖2.2 添加配置2.3 项目中使用之简单使用 (举例讲解)2.4 项目中使用之工具类封装 (正式用这个)2.5 序列化 &…...