手写RPC框架--3.搭建服务注册与发现目录结构

news2025/1/20 18:28:19

RPC框架-Gitee代码(麻烦点个Starred, 支持一下吧)
RPC框架-GitHub代码(麻烦点个Starred, 支持一下吧)

搭建服务注册与发现目录结构

  • 搭建服务注册与发现目录结构
    • a.基于ZooKeeper的服务发现
    • b.搭建基础工程
    • c.基础代码
    • d.编写架子工程
    • e.创建zookeeper基础目录结构

搭建服务注册与发现目录结构

工程一旦变成一个微服务的工程,就意味着每一个特定的服务可能存在多个实例,就是同一个服务部署在多个节点上,这样可以提升服务的可用性,也就是挂了一个还能接着用

在这里插入图片描述

1.服务注册:在服务提供方启动的时候,将对外暴露的接口注册到注册中心中,注册中心将这个服务节点的IP和接口保存下来

2.服务订阅:在服务调用方启动的时候,去注册中心查找并订阅服务提供方的IP,然后缓存到本地,并用于后续的远程调用。

a.基于ZooKeeper的服务发现

服务发现的本质,就是完成接口跟服务提供者IP直接的映射。注册中心也需要完成实时变更推送

利用ZooKeeper的Watcher机制完成服务订阅与服务发现功能

b.搭建基础工程

1.创建目录

  • demo下的api / client-demo / provider-demo
    • consumer-demo 和 provider-demo 都需要导入api的module
  • framework下的common / core
  • manager

在这里插入图片描述

2.在父工程中,编写基础的依赖管理

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.dcy</groupId>
    <artifactId>dcyrpc</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <modules>
        <module>dcyrpc-demo</module>
        <module>dcyrpc-framework</module>
        <module>dcyrpc-framework/dcyrpc-common</module>
        <module>dcyrpc-framework/dcyrpc-core</module>
        <module>dcyrpc-manager</module>
    </modules>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <netty.version>4.1.89.Final</netty.version>
        <junit.version>4.13.2</junit.version>
        <zookeeper.version>3.8.2</zookeeper.version>
        <logback.version>1.4.8</logback.version>
        <fastjson2.version>2.0.34</fastjson2.version>
        <commons-lang3.version>3.12.0</commons-lang3.version>
        <lombok.version>1.18.8</lombok.version>
    </properties>

    <dependencies>
    </dependencies>

    <!--父工程版本管理-->
    <dependencyManagement>
        <dependencies>
            <!--netty-->
            <dependency>
                <groupId>io.netty</groupId>
                <artifactId>netty-all</artifactId>
                <version>${netty.version}</version>
            </dependency>
            <!--junit-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>
            <!--zookeeper-->
            <dependency>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
                <version>${zookeeper.version}</version>
            </dependency>
            <!--logback-->
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>${logback.version}</version>
            </dependency>
            <!--fastjson-->
            <dependency>
                <groupId>com.alibaba.fastjson2</groupId>
                <artifactId>fastjson2</artifactId>
                <version>${fastjson2.version}</version>
            </dependency>
            <!--commons-lang3-->
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>${commons-lang3.version}</version>
            </dependency>
            <!--lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

3.创建.gitignore文件 (通用)

HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**
!**/src/test/**

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
logs/

### VS Code ###
.vscode/

4.commit and push to master

5.创建dev分支,切换至dev

6.在core的pom.xml中,添加netty依赖

7.提交dev分支

8.后期开发均使用dev分支

c.基础代码

1.在api模块下创建DcyRpc接口

package com.dcyrpc;

public interface DcyRpc {

    /**
     * 通用接口,server和client都需要依赖
     * @param msg 发送的具体消息
     * @return
     */
    String sayHi(String msg);

}

2.在provider-demo中创建impl包的 DcyRpcImpl类 实现接口

package com.dcyrpc.impl;

public class DcyRpcImpl implements DcyRpc {
    @Override
    public String sayHi(String msg) {
        return "hi consumer: " + msg;
    }
}

3.在服务提供方provider-demo中创建Application

package com.dcyrpc;

public class Application {
    public static void main(String[] args) {
        // 服务提供方:需要注册服务,启动服务
        // 1.封装要发布的服务
        ServiceConfig<DcyRpc> service = new ServiceConfig<>();
        service.setInterface(DcyRpc.class);
        service.setRef(new DcyRpcImpl());

        // 2.定义注册中心

        // 3.通过启动引导程序,启动服务提供方
        //  (1) 配置 -- 服务应用的名称 / 注册中心 / 序列化协议 / 压缩方式
        //  (2) 发布服务
        DcyRpcBootstrap.getInstance()
                // 配置应用名称
                .application("first-dcyrpc-provider")
                // 配置注册中心
                .registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))
                // 配置服务使用的协议
                .protocol(new ProtocolConfig("jdk"))
                // 发布服务
                .publish(service)
                // 启动服务
                .start();

    }
}

4.在服务消费者consumer-demo中创建Application

package com.dcyrpc;

public class Application {
    public static void main(String[] args) {
        // 服务消费者:获取代理对象
        // 使用ReferenceConfig进行封装
        // reference一定用生成代理的模板方法,get()
        ReferenceConfig<DcyRpc> reference = new ReferenceConfig<>();
        reference.setInterface(DcyRpc.class);

        // 代理做些什么:
        //   1.连接注册中心
        //   2.拉取服务列表
        //   3.选择一个服务并建立连接
        //   4.发送请求:携带一些信息(接口名,参数列表,方法名),获得结果
        DcyRpcBootstrap.getInstance()
                .application("first-dcyrpc-consumer")
                .registry(new RegistryConfig("zookeeper://127.0.0.1:2181"))
                .reference(reference);

        // 获取一个代理对象
        DcyRpc dcyRpc = reference.get();
        dcyRpc.sayHi("你好");
    }
}

d.编写架子工程

1.在framework下的core模块中,定义 DcyRpcBootstrap

@Slf4j
public class DcyRpcBootstrap {

    private static DcyRpcBootstrap dcyRpcBootstrap = new DcyRpcBootstrap();

    private DcyRpcBootstrap(){
        // 构造启动引导程序时,需要做一些什么初始化的事
    }

    public static DcyRpcBootstrap getInstance() {
        return dcyRpcBootstrap;
    }

    /**
     * 定义当前应用的名字
     * @param applicationName 应用名称
     * @return
     */
    public DcyRpcBootstrap application(String applicationName) {
        return this;
    }

    /**
     * 配置一个注册中心
     * @param registryConfig 注册中心
     * @return this
     */
    public DcyRpcBootstrap registry(RegistryConfig registryConfig) {
        return this;
    }

    /**
     * 配置当前暴露的服务使用的协议
     * @param protocolConfig 协议的封装
     * @return this
     */
    public DcyRpcBootstrap protocol(ProtocolConfig protocolConfig) {
        if (log.isDebugEnabled()) {
            log.debug("当前工程使用了:{}协议进行序列化", protocolConfig.toString());
        }
        return this;
    }

    /**
     * --------------------------------服务提供方的相关api--------------------------------
     */

    /**
     * 发布服务:将接口与匹配的实现注册到服务中心
     * @param service 封装需要发布的服务
     * @return
     */
    public DcyRpcBootstrap publish(ServiceConfig<?> service) {
        if (log.isDebugEnabled()) {
            log.debug("服务{},已经被注册", service.getInterface().getName());
        }
        return this;
    }

    /**
     * 批量发布服务
     * @param service 封装需要发布的服务集合
     * @return this
     */
    public DcyRpcBootstrap publish(List<?> service) {
        return this;
    }

    /**
     * 启动netty服务
     */
    public void start() {
    }


    /**
     * --------------------------------服务调用方的相关api--------------------------------
     */

    public DcyRpcBootstrap reference(ReferenceConfig<?> reference) {
        // 配置reference,将来调用get方法时,方便生成代理对象
        return this;
    }

}

2.在framework下的core模块中,定义 ServiceConfig

public class ServiceConfig<T> {

    // 接口
    private Class<T> interfaceProvider;

    // 具体实现
    private Object ref;

    public Class<T> getInterface() {
        return interfaceProvider;
    }

    public void setInterface(Class<T> interfaceProvider) {
        this.interfaceProvider = interfaceProvider;
    }

    public Object getRef() {
        return ref;
    }

    public void setRef(Object ref) {
        this.ref = ref;
    }
}

3.在framework下的core模块中,定义 ReferenceConfig

public class ReferenceConfig<T> {

    private Class<T> interfaceRef;

    public Class<T> getInterface() {
        return interfaceRef;
    }

    public void setInterface(Class<T> interfaceConsumer) {
        this.interfaceRef = interfaceConsumer;
    }

    public T get() {
        // 使用动态代理完成工作
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Class[] classes = new Class[]{interfaceRef};

        // 使用动态代理生成代理对象
        Object helloProxy = Proxy.newProxyInstance(classLoader, classes, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("hello proxy");
                return null;
            }
        });

        return (T) helloProxy;
    }
}

4.在framework下的core模块中,定义 RegistryConfig

public class RegistryConfig {

    private String connectString;

    public RegistryConfig(String connectString) {
        this.connectString = connectString;
    }
}

5.在framework下的core模块中,定义 ServiceConfig

public class ServiceConfig<T> {
    // 接口
    private Class<T> interfaceProvider;
    // 具体实现
    private Object ref;

    public Class<T> getInterface() {
        return interfaceProvider;
    }

    public void setInterface(Class<T> interfaceProvider) {
        this.interfaceProvider = interfaceProvider;
    }

    public Object getRef() {
        return ref;
    }

    public void setRef(Object ref) {
        this.ref = ref;
    }
}

6.在provider-demo下的resources目录中,创建logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="pattern" value="%d{HH:mm:ss.SSS} %msg%n"/>
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!--输出流对象 默认 System.out 改成 System.err-->
        <target>System.out</target>
        <!--日志格式配置-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>
    <root level="debug">
        <appender-ref ref="console"/>
    </root>
</configuration>

e.创建zookeeper基础目录结构

注册服务

dcyrpc-metadata (持久节点)
 L providers (持久节点)
 	L service (持久节点:接口的全限定名)
 		L node1 [data]	(节点名:ip:port) (数据:相关的特性数据/配置等)
 		L node2 [data]
 		L node3 [data]
 L consumers
 	L service
 		L node1 [data]
 		L node2 [data]
 		L node3 [data]
 L config
.........

1.导入相关的依赖包到相关的module中

<dependencies>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.zookeeper</groupId>
        <artifactId>zookeeper</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>com.dcy</groupId>
        <artifactId>dcyrpc-common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

2.在manager的com.dcyrpc下创建Application

/**
 * 注册中心的管理页面
 */
@Slf4j
public class Application {
    public static void main(String[] args) {

        // 创建zookeeper实例
        ZooKeeper zooKeeper = ZookeeperUtils.createZookeeper();

        // 定义节点和数据
        String basePath = "/dcyrpc-metadata";
        String providePath = basePath + "/providers";
        String consumerPath = basePath + "/consumers";

        ZookeeperNode baseNode = new ZookeeperNode(basePath, null);
        ZookeeperNode provideNode = new ZookeeperNode(providePath, null);
        ZookeeperNode consumerNode = new ZookeeperNode(consumerPath, null);

        // 创建节点
        List.of(baseNode, provideNode, consumerNode).forEach(node -> {
            ZookeeperUtils.createNode(zooKeeper, node, null, CreateMode.PERSISTENT);
        });

        // 关闭连接
        ZookeeperUtils.close(zooKeeper);
    }
}

3.在common的com.dcyrpc下创建exceptions工具类:exceptions.ZookeeperException类 (未写具体内容)

public class ZookeeperException extends RuntimeException{
}

4.在common的com.dcyrpc下创建Constants

public class Constant {
    // zookeeper的默认连接地址
    public static final String DEFAULT_ZK_CONNECT = "127.0.0.1:2181";

    // zookeeper默认的超时时间
    public static final int TIME_OUT = 10000;
}

5.在common的com.dcyrpc下创建utils.zookeeper工具类:ZookeeperUtils

@Slf4j
public class ZookeeperUtils {

    /**
     * 使用默认配置创建Zookeeper实例
     * @return zookeeper实例
     */
    public static ZooKeeper createZookeeper() {
        // 定义连接参数
        String connectString = Constant.DEFAULT_ZK_CONNECT;
        // 定义超时时间 10秒
        int sessionTimeout = Constant.TIME_OUT;

        return createZookeeper(connectString, sessionTimeout);
    }

    /**
     * 使用自定义配置创建Zookeeper实例
     * @param connectionString
     * @param timeout
     * @return
     */
    public static ZooKeeper createZookeeper(String connectionString, int timeout) {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        // 定义连接参数
        String connectString = Constant.DEFAULT_ZK_CONNECT;
        // 定义超时时间 10秒
        int sessionTimeout = Constant.TIME_OUT;

        try {
            // 创建zookeeper实例,简历连接
            final ZooKeeper zooKeeper = new ZooKeeper(connectString, sessionTimeout, event -> {
                if (event.getState() == Watcher.Event.KeeperState.SyncConnected) {
                    System.out.println("客户端连接成功");
                    countDownLatch.countDown();
                }
            });

            // 等待连接成功
            countDownLatch.await();
            
            return zooKeeper;
        } catch (IOException | InterruptedException e) {
            log.error("创建zookeeper实例时发生异常:",e);
            throw new ZookeeperException();
        }
    }
}

5.在common的com.dcyrpc下创建节点工具类:ZookeeperNode

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ZookeeperNode {

    private String nodePath;
    private byte[] data;

}

6.在ZookeeperUtils类中创建createNode方法:创建zookeeper的节点的工具方法

/**
 * 创建zookeeper的节点的工具方法
 * @param zooKeeper zookeeper实例
 * @param node 节点
 * @param watcher watcher实例
 * @param createMode 节点的类型
 * @return true:成功创建  false:已经存在  异常:抛出
 */
public static Boolean createNode(ZooKeeper zooKeeper, ZookeeperNode node, Watcher watcher, CreateMode createMode) {
    try {
        if (zooKeeper.exists(node.getNodePath(), watcher) == null){
            String result = zooKeeper.create(node.getNodePath(), node.getData(), ZooDefs.Ids.OPEN_ACL_UNSAFE, createMode);
            log.info("节点{},已经成功创建", result);
            return true;
        }else {
            if (log.isDebugEnabled()) {
                log.info("节点{},已经存在,无需创建", node.getNodePath());
            }
        }
    } catch (KeeperException | InterruptedException e) {
        log.error("创建基础目录时,产生异常,如下:",e);
        throw new ZookeeperException();
    }

    return false;
}

7.在ZookeeperUtils类中创建close方法:关闭zookeeper的方法

/**
 * 关闭zookeeper
 * @param zooKeeper
 */
public static void close(ZooKeeper zooKeeper) {
    try {
        zooKeeper.close();
    } catch (InterruptedException e) {
        log.error("关闭zookeeper时发生异常:",e);
        throw new ZookeeperException();
    }
}

8.在manager的resource目录下创建logback.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="pattern" value="%d{HH:mm:ss.SSS} %msg%n"/>
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!--输出流对象 默认 System.out 改成 System.err-->
        <target>System.out</target>
        <!--日志格式配置-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>
    <root level="debug">
        <appender-ref ref="console"/>
    </root>
</configuration>

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

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

相关文章

ISP——3A算法

目录 前沿一. 自动曝光AE1.1. 自动曝光1.2. 18%灰1.3. 测光区域1.4. 摄影曝光加法系统1.5. AE算法1.5.1. 考虑事项1.5.2. AE实现过程 1.6. AE算法 二. 自动对焦AF2.1. 什么是自动对焦2.2. 图像清晰度评价方法2.2.1. Brenner 梯度函数2.2.2. Tenengrad 梯度函数2.2.3. Laplacian…

设计模式-8--模板方法模式(Template Method Pattern)

一、什么是模板方法模式&#xff08;Template Method Pattern&#xff09; 模板方法模式&#xff08;Template Method Pattern&#xff09;是一种行为型设计模式&#xff0c;它定义了一个算法的骨架&#xff0c;将一些步骤的实现延迟到子类中。模板方法模式允许在不改变算法的…

多机单目标跟踪Cross-Drone Transformer Network for Robust Single Object Tracking

1. 摘要 无人机已被广泛用于各种应用&#xff0c;如空中摄影和军事安全&#xff0c;因为与固定摄像机相比&#xff0c;无人机具有高机动性和广阔的视野。多架无人机跟踪系统可以通过收集不同视角的互补视频片段来提供丰富的目标信息&#xff0c;特别是当目标在某些视角下被遮挡…

微信小程序分包-主包尺寸 (不包合插件) 应小于 1.5 M

目录 起因 分包 最后 起因 更新一个之前的小程序&#xff0c; 上传的时候提示主包尺寸 (不包合插件) 应小于 1.5 M 怎么办&#xff1f;查看教程啊 开发者可通过开发者工具中的性能扫描工具提前发现代码中的可优化项&#xff1a; 1. 代码包不包含插件大小超过 1.5 M 【建议】…

1777_树莓派截图功能实现

全部学习汇总&#xff1a; GitHub - GreyZhang/little_bits_of_raspberry_pi: my hacking trip about raspberry pi. 最近入手了树莓派的3B版本&#xff0c;安装了官方推荐的Debian版本。之前使用Linux的几个发行版本的时候&#xff0c;系统中通常会有KDE等集成的截图工具&…

第一个实例:QT实现汽车电子仪表盘

1.实现效果 1.1.视频演示 QT 实现汽车仪表盘 1.2.实现效果截图 2.生成的安装程序 此程序是个windows下的安装程序,可以直接安装,看到汽车仪表盘的实现效果,安装程序从下面链接下载: 【免费】使用QT实现的汽车电子仪表盘,在windows下的安装程序资源-CSDN文库 3.功能概述…

go语言基础操作--二

a : 10str : "mike"//匿名函数&#xff0c;没有函数名字 形成一个闭包,函数定义&#xff0c;还没有调用f1 : func() { //:自动推到类型fmt.Println("a ", a)fmt.Println("str ", str)}f1()//给一个函数类型起别名 这个写法不推荐type FuncType …

存储成本降低85%,携程历史库场景的降本实践

携程&#xff0c;一家中国领先的在线票务服务公司&#xff0c;从 1999 年创立至今&#xff0c;数据库系统历经三次替换。在移动互联网时代&#xff0c;面对云计算卷积而来的海量数据&#xff0c;携程通过新的数据库方案实现存储成本降低 85% 左右&#xff0c;性能提升数倍。本文…

TensorRT来加速YOLO v5推理与检测

TensorRT来加速YOLO v5推理与检测 文章目录 TensorRT来加速YOLO v5推理与检测TensorRT简介一、TensorRT安装1. 电脑基础环境2. 查看 cuda 版本3. 安装TensorRT4. 验证TensorRT 二、YOLO v5模型导出函数1.onnx 模型导出&#xff08;def export_onnx&#xff08;&#xff09;函数…

nmon性能监控工具介绍【杭州多测师_王sir】

nmon监控工具 (nmon监控centos6X)1) 工具准备nmon16X(性能监控)和nmon_analyser(性能报告分析器)&#xff1b;2) 下载nmon页面地址:http://nmon.sourceforge.net/pmwiki.php?nSite.Download3) 下载指定版本nmon到centos6X:wget https://nchc.dl.sourceforge.net/project/nmon…

HTTP协议初识·中篇

加上目录&#xff0c;会出现导向不正确的情况&#xff0c;可能是bug&#xff0c;目录一长就容易出错&#xff1f; 本篇主要讲解了&#xff1a; 网页分离(网页代码和.c文件分离) html链接跳转 网页添加图片 确认并返回资源类型 填写正文长度属性 添加表单 临时重定向 补充知识&a…

04. 函数和函数调用机制

1. 先学习/复习C语言的入门知识 1.1 C语言简介 C语言是一种通用的编程语言&#xff0c;于1972年由丹尼斯里奇&#xff08;Dennis Ritchie&#xff09;创建。C语言最初目的是为了开发UNIX操作系统&#xff0c;但由于其简洁的语法、快速的执行速度和可移植性&#xff0c;自此成…

Linux系统中驱动入门设备树DTS(经典)

设备树&#xff08;DTS:device tree source&#xff09;&#xff0c;字面意思就是一块电路板上设备如上图中CPU、DDR、I2C、GPIO、SPI等&#xff0c;按照树形结构描绘成的一棵树。按照策略和功能分离的思路&#xff0c;就是驱动代码&#xff08;功能&#xff09;和设备树DTS配置…

算法通关村16关 | 滑动窗口如此简单

1. 滑动窗口基本思想 滑动窗口就是快慢指针问题&#xff0c;快指针走一步&#xff0c;慢指针走一步&#xff0c;维护窗口的大小。 下图有大小为3的滑动窗口移动示例。 窗口&#xff1a;窗口是left和right之间的元素&#xff0c;也可以理解为一个区间。窗口的大小可能固定&#…

Win7设备和打印机里空白,0个对象,但是可以打印的处理办法

呉師傅 你是不是遇到过Win7系统打开设备和打印机的时候显示是空白的&#xff0c;0个设备的情况&#xff1f;要怎么操作才能解决这一问题呢&#xff0c;下面就分享一下如何处理这个问题的小方法大家可以尝试一下。 问题如下&#xff1a; 解决方法&#xff1a; 1、点击桌面左下…

Unity中Shader的消融视觉效果优化smoothstep(min,max,x)

文章目录 前言Unity中Shader的消融视觉效果优化 一、在clip(value) 的 基础上 用 smoothstep(min,max,x)&#xff0c;并且增加一个渐变纹理对消融边缘进行视觉上的优化二、进行优化 前言 Unity中Shader的消融视觉效果优化 一、在clip(value) 的 基础上 用 smoothstep(min,max…

3.0 Dubbo的可扩展机制SPI源码解析

1. Dubbo SPI 架构图 2. Demo ExtensionLoader<Protocol> extensionLoader ExtensionLoader.getExt ensionLoader(Protocol.class); Protocol http extensionLoader.getExtension("dubbo"); System.out.println(http); 上⾯这个Demo就是Dubbo常⻅的写法&am…

java八股文面试[多线程]——newWorkStealingPool

newWorkStealingPool是什么&#xff1f; newWorkStealingPool简单翻译是任务窃取线程池。 newWorkStealingPool 是Java8添加的线程池。和别的4种不同&#xff0c;它用的是ForkJoinPool。 使用ForkJoinPool的好处是&#xff0c;把1个任务拆分成多个“小任务”&#xff0c;把这…

【MySQL学习笔记】(七)内置函数

内置函数 日期函数示例案例-1案例-2 字符串函数示例 数学函数其他函数 日期函数 示例 获得当前年月日 mysql> select current_date(); ---------------- | current_date() | ---------------- | 2023-09-03 | ---------------- 1 row in set (0.00 sec)获得当前时分秒…

剑指 Offer 57 - II. 和为s的连续正数序列(简单)

题目&#xff1a; class Solution { public:vector<vector<int>> findContinuousSequence(int target) { //本题使用滑动窗口&#xff08;双指针&#xff09;int i1, j1; //定义左右边界&#xff0c;一般是左闭右开int sum0; //窗口内的和vector&…