Dubbo

Dubbo

dubbo是阿里巴巴的开源的高性能的java RPC框架,现在是Apache基金会的管理与支持之下,可以实现如服务发现、负载均衡、流量调度等服务治理诉求。dubbo的微服务解决方案DNS(dubbo、nacos、sentinel)
官网地址:https://cn.dubbo.apache.org/zh-cn/

dubbo架构

  1. provider 功能提供者
  2. consumer 功能调用者、消费者
  3. commons-api 通用内容,是consumer和provider中,公共使用的实体类、service的接口,通过maven依赖引用的方式,引入到需要的项目中
  4. registry 注册中心,可用可不用,但是如果有多个provider,做了集群,其中哪个挂了,consumer调用的话就会出问题,而注册中心可以做健康检查,通过负载均衡的轮询或者其它机制,调用健康的provder,保证服务的正常调用。
    在这里插入图片描述

spring直连方式consumer调用provider

这里开始采用spring的方式,手动通过xml文件,发布接口

dubbo父工程

目录结构:
在这里插入图片描述

  • 父工程引入的依赖:
<?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.wzw</groupId>
    <artifactId>dubbo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
        <module>dubbo-api</module>
        <module>dubbo-provider</module>
        <module>dubbo-consumer</module>
    </modules>

    <properties>
        <maven.compiler.source>22</maven.compiler.source>
        <maven.compiler.target>22</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

	<!-- 父工程依赖管理 -->
    <dependencyManagement>
        <dependencies>
            <!-- 日志 -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.36</version>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>1.2.3</version>
            </dependency>
            <!-- lombok -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.30</version>
            </dependency>
            <!-- dubbo -->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo</artifactId>
                <version>3.2.0-beta.4</version>
            </dependency>

            <dependency>
                <groupId>org.apache.curator</groupId>
                <artifactId>curator-x-discovery</artifactId>
                <version>4.3.0</version>
            </dependency>

            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.0.2.RELEASE</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

dubbo-api

通用内容,是consumer和provider中,公共使用的实体类、service的接口,通过maven依赖引用的方式,引入到需要的项目中,不用启动,main方法可以删

  • 目录结构
    在这里插入图片描述
  • User
package com.wzw.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User implements Serializable {

    private String name;
    private String password;
}

  • UserService
package com.wzw.service;

public interface UserService {

    boolean login(String name,String password);
}

dubbo-provider

  • 引入依赖
<dependencies>
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo</artifactId>
                <version>3.2.0-beta.4</version>
            </dependency>

            <dependency>
                <groupId>org.apache.curator</groupId>
                <artifactId>curator-x-discovery</artifactId>
                <version>4.3.0</version>
            </dependency>
            <dependency>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
                <version>3.8.0</version>
                <exclusions>
                    <exclusion>
                        <groupId>io.netty</groupId>
                        <artifactId>netty-handler</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>io.netty</groupId>
                        <artifactId>netty-transport-native-epoll</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
  • 目录结构
    在这里插入图片描述
  • UserServiceImpl
package com.wzw.service;

public class UserServiceImpl implements UserService {
    @Override
    public boolean login(String name, String password) {
    	//输出当前类名全路径,调用的时候好观察
        System.out.println("com.wzw.service.UserServiceImpl:"+name+"-"+password);
        return false;
    }
}

  • DubboProviderMain
package com.wzw;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.concurrent.CountDownLatch;

public class DubboProviderMain {

    private static final Logger logger = LoggerFactory.getLogger(DubboProviderMain.class);
    public static void main(String[] args) throws InterruptedException {
        //加载xml的配置文件,点击applicationContext-provider.xml,如果能跳转,路径就没问题
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-provider.xml");
        //启动
        context.start();
        logger.info("1111");
        //因为是main,运行完就结束了,所以要阻塞,让它一直提供服务,如果是springboot,它是一直运行的,所以springboot不用阻塞
        new CountDownLatch(1).await();
    }
}
  • applicationContext-provider.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <dubbo:application name="dubbo-provider"/>

    <!-- 使用dubbo协议 端口20880-->
    <dubbo:protocol name="dubbo" port="20880"/>

    <!-- 创建接口对象 -->
    <bean id="userService" class="com.wzw.service.UserServiceImpl"/>

    <!-- 发布接口,要发布的是上面创建的userService,以提供consumer来调用-->
    <dubbo:service interface="com.wzw.service.UserService" ref="userService"/>

</beans>

  • 启动服务,观察控制台,发布RPC服务接口成功
    在这里插入图片描述
    在这里插入图片描述

springboot直连方式consumer调用provider

直接在dubbo工程下新建两个springboot项目,dubbo-boot-provider和dubbo-boot-consumer

  • 目录结构
    在这里插入图片描述

dubbo-boot-provider

  • 目录结构
    在这里插入图片描述

  • 引入依赖

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.12</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.wzw</groupId>
    <artifactId>dubbo-boot-provider</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>dubbo-boot-provider</name>
    <description>dubbo-boot-provider</description>

    <dependencies>

        <!--dubbo-api-->
        <dependency>
            <groupId>com.wzw</groupId>
            <artifactId>dubbo-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!--dubbo-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>3.3.0-beta.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

  • 配置文件
spring:
  application:
    name: dubbo-boot-provider

dubbo:              # dubbo 是根节点,表示Dubbo的相关配置
  protocol:         # protocol 是子节点,表示协议配置;
    name: dubbo     # name 是protocol的属性,表示协议的名称
    port: -1        # port 是protocol的属性,表示协议端口
  • UserServiceImpl
package com.wzw.dubbobootprovider.service;

import com.wzw.service.UserService;
import org.apache.dubbo.config.annotation.DubboService;

@DubboService
public class UserServiceImpl implements UserService {
    @Override
    public boolean login(String name, String password) {
        System.out.println("dubbo-boot-provider   UserServiceImpl:"+name+"  "+password);
        return false;
    }
}

  • DubboBootProviderApplication
    启动类只加了@EnableDubbo表示开启dubbo
package com.wzw.dubbobootprovider;

import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@EnableDubbo
@SpringBootApplication
public class DubboBootProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(DubboBootProviderApplication.class, args);
    }

}

  • 小结
    • 配置类
      添加dubbo配置信息
      dubbo:              # dubbo 是根节点,表示Dubbo的相关配置
        protocol:         # protocol 是子节点,表示协议配置;
          name: dubbo     # ;name 是protocol的属性,表示协议的名称
          port: -1        # port 是protocol的属性,表示协议端口
      
    • 要发布的接口,类名上使用@DubboService
    • 启动类添加@EnableDubbo

dubbo扫描类的方式

  1. 启动类上面使用@EnableDubbo,会自动扫描包及其子包下的@DubboService修饰的类。
  2. 使用@DubboComponentScan,如果要发布的接口不在@EnableDubbo的包及其子包下,可以在启动类上面使用@DubboComponentScan(basePackages={"com.wzw.service"})来显示指定扫描的类。
  3. 配置文件中通过使用dubbo.scan.base-packages=com.wzw.dubbobootprovider.service来指定要扫描的类,等同于@EnableDubbo

dubbo-boot-consumer

  • 引入依赖
<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.12</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.wzw</groupId>
    <artifactId>dubbo-boot-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>dubbo-boot-consumer</name>
    <description>dubbo-boot-consumer</description>

    <dependencies>

        <!--dubbo-api-->
        <dependency>
            <groupId>com.wzw</groupId>
            <artifactId>dubbo-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!--dubbo-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>3.3.0-beta.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

  • 配置文件
spring:
  application:
    name: dubbo-boot-consumer

dubbo:                        # Dubbo 协议的根节点            
  application:                # 应用名      
    qos-enable: false         # 是否开启QOS服务     
  • 测试类
    消费者就不用启动服务了,直接测试类调用
package com.wzw.dubbobootconsumer;

import com.wzw.service.UserService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class DubboBootConsumerApplicationTests {

	//这个地址是服务提供者启动的时候,接口发布地址,服务端控制台启动信息中有
    @DubboReference(url = "dubbo://192.168.8.1:20880/com.wzw.service.UserService")
    private UserService userService;

    @Test
    void test1(){
        boolean login = userService.login("zhangsan", "pwd123");
        System.out.println(login);
    }

}

  • 小结
    • 配置类

      dubbo:                        # Dubbo 协议的根节点
        application:                # 应用名
          qos-enable: false         # 是否开启QOS服务
      
    • 调用接口的地方需要使用@DubboReference,并指定服务端提供的接口地址

      @DubboReference(url = "dubbo://192.168.8.1:20880/com.wzw.service.UserService")
      

测试

  • 启动provider
    查看控制台,这里有发布的dubbo的接口,这个接口地址,消费者调用的时候配置在@DubboReference
@DubboReference(url = "dubbo://192.168.8.1:20880/com.wzw.service.UserService")

在这里插入图片描述
执行consumer的测试方法,查看结果

  • 这是provider接口的输出,成功调取
    在这里插入图片描述
  • consumer的调用方法输出,也成功获取到返回结果
    在这里插入图片描述

通过nacos注册中心调用dubbo发布的接口

  • 目录结构
    在这里插入图片描述
  • 引入依赖,consumer比provider多一个web依赖,其它的依赖都是一样的,通过consumer的controller来测试的,如果还是测试类测试的话,不引入web也行
		<!--公共的service模块依赖-->
        <dependency>
            <groupId>com.wzw</groupId>
            <artifactId>dubbo-api</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
        
        <!-- web应用依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- nacos依赖 -->
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
            <version>2.3.2</version>
        </dependency>

        <!--dubbo-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>3.3.0-beta.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
  • 配置文件,consumer和provider的配置文件也是一样的,也可以配置其它参数,但是这里只是简单测试,只需要配置nacos的地址就行
    在这里插入图片描述
  • provider的代码,service类用的是@DubboService,然后启动类记得加@EnableDubbo
    在这里插入图片描述
  • consumer代码,service类用的是@DubboReference
    在这里插入图片描述
  • 启动调用接口
    在这里插入图片描述
    调用返回成功,查看控制台输出也是正常
    在这里插入图片描述

dubbo-admin

  • dubbo-admin管理平台,是图形化的服务管理页面,从注册中心获取所有提供者和消费者进行配置管理。没有正式发行。
    官方介绍
    在这里插入图片描述
  • dubbo-adminq其实就是一个项目,前端是vue,后端是springboot,部署运行就可以

其它

dubbo不能传输文件对象和inputstream

Dubbo 中传递 File 对象和 InputStream 有一些特定的注意事项,因为这些对象通常是与文件系统或网络流相关的资源,不适合直接在网络中传输。
解决:使用byte[]传输
提供服务方:alioss为例

   /**
     * 上传文件到OSS
     * @param objectName    存储名称
     * @param bytes   字节对象
     * @return  returnType=url 返回url文件地址 returnType=fileName 返回文件路径
     * @throws Exception
     */
    public String uploadFile(String objectName, byte[] bytes, UploadSetupBody uploadSetupBody) {

        OSS ossClient=new OSSClientBuilder().build(uploadSetupBody.getEndpoint(), uploadSetupBody.getAccessKeyId(), uploadSetupBody.getAccessKeySecret());

        //访问文件的临时路径
        String url="";

        try(InputStream inputStream=new java.io.ByteArrayInputStream(bytes)) {
            // 创建PutObjectRequest对象。
            PutObjectRequest putObjectRequest = new PutObjectRequest(uploadSetupBody.getBucketName(), objectName, inputStream);

            // 上传字符串。
            PutObjectResult result = ossClient.putObject(putObjectRequest);

            url="https://"+uploadSetupBody.getBucketName()+"."+uploadSetupBody.getEndpoint().substring(uploadSetupBody.getEndpoint().lastIndexOf("/")+1)+"/"+objectName;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return objectName;
    }

服务调用方:

    /**
     * 通用上传请求(单个)
     * return 文件访问url
     */
    @PostMapping("/upload")
    public AjaxResult uploadFile(MultipartFile file) throws Exception {
        try {
            //云存储服务配置,AccessKeyID、Bucket、Endpoint等
            UploadSetupBody uploadSetupBody = storageServerService.selectWycStorageServerConfig("1");
            if(uploadSetupBody==null){
                return AjaxResult.error("未获取到存储服务");
            }
            //构建文件全路径
            String objectName = uploadSetupBody.getPreFix()+"/" + DateUtils.datePath() + "/" + storageServerService.createFileName(file.getOriginalFilename());
            //返回全路径
            String filePath = storageServerService.uploadFile(objectName, file.getBytes(),uploadSetupBody);
            //根据路径获取url
            String url = storageServerService.getOssUrl(filePath,uploadSetupBody);
            Map<String, String> map = new HashMap<>();
            map.put("url", url);
//            map.put("filePath", objectName);
            AjaxResult ajax = AjaxResult.success(map);

            return ajax;
        } catch (Exception e) {
            return AjaxResult.error(e.getMessage());
        }
    }

解决dubbo序列化前置问题,在消费端配置

dubbo:
	consumer: 
		filter: -authenticationPrepare,-contextHolderParametersSelectedTransfer

参数的传递

  • consumer存参数
//存字符串
RpcContext.getClientAttachment().setAttachment("name", "张三")
//存对象
RpcContext.getClientAttachment().setObjectAttachment("loginUser", SecurityUtils.getLoginUser())
  • provider取参数
//取字符串
RpcContext.getServerContext().getAttachment("name");
//取对象
RpcContext.getServerContext().getObjectAttachment("loginUser");

org.apache.dubbo.common.serialize.SerializationException: java.lang.UnsupportedOperationException: CollectionDeserializer[interface java.util.List], dubbo version: 3.3.0-beta.4, current host: 192.168.1.161, error code: 4-20. This may be caused by , go to https://dubbo.apache.org/faq/4/20 to find instructions. java.io.IOException: org.apache.dubbo.common.serialize.SerializationException: java.lang.UnsupportedOperationException: CollectionDeserializer[interface java.util.List]

错误信息:
在这里插入图片描述
看着像是List的问题,但是跟List没关系,是因为在实体属性加了校验的注释,但是controller的实体入参的地方,没有加@Validated,加上@Validated就好了

version

调用对用的版本

@DubboService(version = "1.0.0")
public class UserServiceImpl implements UserService {
....
}
@DubboService(version = "1.0.1")
public class UserServiceImpl2 implements UserService {
.....
}
	//调用的就是对应版本1.0.1,UserServiceImpl2 
    @DubboReference(version = "1.0.1")
    private UserService userService;

应用级和接口级服务发现

不配置的情况下,dubbo3.0默认是all(接口级+应用级均注册)
在这里插入图片描述

  • 仅应用级注册instance
# 仅应用级注册
dubbo:
    registry:
        register-mode: instance

在这里插入图片描述

  • 仅接口级注册
dubbo:              # dubbo 是根节点,表示Dubbo的相关配置
  registry:
    address: nacos://192.168.8.1:8848     # dubbo注册到nacos的地址
    register-mode: interface

在这里插入图片描述

Triple 协议

dubbo支持的协议

在这里插入图片描述

Triple

基于 HTTP/2 并且完全兼容 gRPC 协议,可以直接使用 curl、浏览器访问后端 Dubbo 服务

  • 设置协议,服务提供者提供triple协议的访问
dubbo:          
  protocol:
    name: tri		#triple协议
    port: 5000		#端口

调用方式

一般的这种请求,就是Unary 请求

  • 提供服务
@DubboService
public class UserServiceImpl implements UserService {
    @Override
    public boolean login(String name, String password) {
        System.out.println("dubbo-boot-nacos-provider   UserServiceImpl:"+name+"  "+password);
        return false;
    }
}
  • 调用
@RestController
public class ConsumerController {

    @DubboReference
    private UserService userService;

    @GetMapping("/testDubbo")
    public boolean testDubbo()
    {
        return userService.login("wzw","123456");
    }

}

Streaming 通信模式

  • 接口需要发送大量数据,这些数据无法被放在一个 RPC 的请求或响应中,需要分批发送,但应用层如果按照传统的多次 RPC 方式无法解决顺序和性能的问题,如果需要保证有序,则只能串行发送
  • 流式场景,数据需要按照发送顺序处理, 数据本身是没有确定边界的
  • 推送类场景,多个消息在同一个调用的上下文中被发送和处理

官方:https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/advanced-features-and-usage/triple/streaming/

  • StreamObserver需要用到依赖
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-common</artifactId>
                <version>3.3.0-beta.2</version>
            </dependency>
SERVER_STREAM(服务端流)
  • 接口增加方法
public interface UserService {

    //UNARY
    boolean login(String name,String password);
    //SERVER_STREAM
    default void sayHelloServerStream(String name, StreamObserver<String> response){

    }
    //CLIENT_STREAM
    default StreamObserver<String> sayHelloClientStream(StreamObserver<String> response){
        return response;
    }
}
  • provider
@DubboService
public class UserServiceImpl implements UserService {
    @Override
    public void sayHelloServerStream(String name, StreamObserver<String> response) {
        response.onNext("hello "+name);
        response.onNext("hello "+name);
        response.onNext("hello "+name);
        response.onCompleted();
    }
}
  • consumer
@RestController
public class ConsumerController {

    @DubboReference
    private UserService userService;

    @GetMapping("/sayHello")
    public String sayHello(String name){
        userService.sayHelloServerStream(name,new StreamObserver<String>() {

            @Override
            public void onNext(String data) {
                System.out.println("onNext: " + data);
            }

            @Override
            public void onError(Throwable throwable) {

            }

            @Override
            public void onCompleted() {

            }
        });
        return "success";
    }

}
  • 测试
    在这里插入图片描述
CLIENT_STREAM(客户端流)
BIDIRECTIONAL_STREAM(双向流)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值