springboot整合dubbo


微服务框架间服务的调用建议用dubbo进行实现,因为dubbo性能比feign高。

环境要求:

1、centos7
2、nacos(注册中心)
3、springboot微服务

一、centos7 搭建nacos

Docker启动nacos
shell命令:

docker run --name nacos-standalone -e MODE=standalone -d -p 8848:8848 -p 9848:9848 -p 9849:9849 nacos/nacos-server:2.0.0

地址访问:http://虚拟机ip:8848/nacos
默认账号、密码都是:nacos

二、springboot集成dubbo过程

自己搭的项目可自行下载:
【百度网盘】https://pan.baidu.com/s/1qXvNakjWH1nrjsib3uzlUA 提取码:61hh

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

创建三个springboot微服务,分别如下:

  • springboot-dubbo-provider-api(服务生产者api,提供消费者调用)
  • springboot-dubbo-provider(服务生产者,服务提供方)
  • springboot-dubbo-customer(服务消费者,服务调用方)

新建一个maven工程,添加如下依赖

		<!-- registry dependency -->
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
            <version>${nacos.version}</version>
        </dependency>

        <!-- dubbo dependency-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>${dubbo.version}</version>
        </dependency>

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

发布服务
1、定义服务接口

public interface DevelopService {
    String invoke(String param);
}

2、服务接口实现

@DubboService(group = "group1",version = "0.0.1")
public class DevelopProviderServiceV1 implements DevelopService{
    @Override
    public String invoke(String param) {
        StringBuilder s = new StringBuilder();
        s.append("ServiceV1 param:").append(param);
        return s.toString();
    }
}

使用@DubboService 注解,Dubbo会将对应的服务注册到spring, 在spring启动后调用对应的服务导出方法,将服务注册到注册中心, 这样Consumer端才能发现我们发布的服务并调用

3、添加Dubbo配置
添加application.yml相关配置,内容如下:

dubbo:
  application:
#    qosEnable: false
    logger: slf4j
    name: SpringbootProviderApplication
  registry:
    address: nacos://虚拟机ip:8848
  protocol:
    name: dubbo
    port: 20880
server:
  port: 8080

4、启动服务
创建Springboot启动类,需添加@EnableDubbo注解,开启Dubbo自动配置功能

@EnableDubbo
@SpringBootApplication
public class SpringbootProviderApplication{

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

启动成功后,在注册中心可以看到对应的服务列表,如图:
在这里插入图片描述
调用服务
创建DemoCustomer类,通过@DubboReference注解对需要调用的服务进行引入。即可像调用本地方法一样调用远程服务了。

@RestController
@RequestMapping("dubbo/rpc/")
public class DemoCustomer {
    @DubboReference(group = "group1",version = "0.0.1")
    private DevelopService developService;
    
	@RequestMapping("invoke")
    public String invoke() {
        //调用DevelopService的group1分组实现
		return developService.invoke("1");
    }
}

先后启动springboot-dubbo-prodiver、spring-dubbo-customer服务,调用invoke方法,返回 ServiceV1 param: 1

说明调用成功

三、异步调用

Dubbo异步调用分为Provider端异步调用和Consumer端异步调用。 Provider端异步执行将阻塞的业务从Dubbo内部线程池切换到业务自定义线程, 避免Dubbo线程池的过度占用,有助于避免不同服务间的互相影响。异步执行无异于节省资源或提升RPC响应性能。

Provider 端异步执行和 Consumer 端异步调用是相互独立的,你可以任意正交组合两端配置

  • Consumer同步 - Provider同步
  • Consumer异步 - Provider同步
  • Consumer同步 - Provider异步
  • Consumer异步 - Provider异步

使用场景

provider 异步

1、使用CompletableFuture实现异步
接口定义:

public interface AsyncService {
    /**
     * 同步调用方法
     */
    String invoke(String param);
    /**
     * 异步调用方法
     */
    CompletableFuture<String> asyncInvoke(String param);
}

服务实现:

@DubboService(version = "0.0.1")
public class AsyncServiceImpl implements AsyncService {

    @Override
    public String invoke(String param) {
        //int cnt = 100 / 0;//降级容错测试
        try {
            long time = ThreadLocalRandom.current().nextLong(1000);
            Thread.sleep(time);
            StringBuilder s = new StringBuilder();
            s.append("AsyncService invoke param:").append(param).append(",sleep:").append(time);
            return s.toString();
        }
        catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return null;
    }

    @Override
    public CompletableFuture<String> asyncInvoke(String param) {
        // 建议为supplyAsync提供自定义线程池
        return CompletableFuture.supplyAsync(() -> {
            //int cnt = 100 / 0;//降级容错测试
            try {
                // Do something
                long time = ThreadLocalRandom.current().nextLong(1000);
                Thread.sleep(time);
                StringBuilder s = new StringBuilder();
                s.append("AsyncService asyncInvoke param:").append(param).append(",sleep:").append(time);
                return s.toString();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return null;
        });
    }
}

通过 return CompletableFuture.supplyAsync() ,业务执行已从 Dubbo 线程切换到业务线程,避免了对 Dubbo 线程池的阻塞。

2、使用AsyncContext实现异步
Dubbo 提供了一个类似 Servlet 3.0 的异步接口AsyncContext,在没有 CompletableFuture 签名接口的情况下,也可以实现 Provider 端的异步执行。

接口定义:


public interface AsyncService {
    String sayHello(String name);
}

服务实现:

public class AsyncServiceImpl implements AsyncService {
    public String sayHello(String name) {
        final AsyncContext asyncContext = RpcContext.startAsync();
        new Thread(() -> {
            //int cnt = 100 / 0;//降级容错测试
            
            // 如果要使用上下文,则必须要放在第一句执行
            asyncContext.signalContextSwitch();
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 写回响应
            asyncContext.write("Hello " + name + ", response from provider.");
        }).start();
        return null;
    }
}

降级容错:

public class AsyncMock implements AsyncService {

    @Override
    public String invoke(String param) {
        System.out.println("invoke同步降级容错");
        return "invoke同步降级容错";
    }

    @Override
    public CompletableFuture<String> asyncInvoke(String param) {
        System.out.println("asyncInvoke异步降级容错");
        return null;
    }

    @Override
    public String sayHello(String name) {
        System.out.println("sayHello异步降级容错");
        return "sayHello异步降级容错";
    }
}

消费者端 消费测试:

@RestController
@RequestMapping("async/prc/")
public class AsyncCustomer {
    @DubboReference(
            version = "0.0.1",
            mock = "com.provider.module.dubbo.async.AsyncMock",
            check = false
    )
    private AsyncService asyncService;

    @RequestMapping("asyncInvoke")
    public void asyncInvoke(String param) {
        //调用异步接口
        CompletableFuture<String> future1 = asyncService.asyncInvoke("async call request1");
        future1.whenComplete((v, t) -> {
            if (t != null) {
                t.printStackTrace();
            } else {
                System.out.println("AsyncTask Response-1: " + v);
            }
        });
        //两次调用并非顺序返回
        CompletableFuture<String> future2 = asyncService.asyncInvoke("async call request2");
        future2.whenComplete((v, t) -> {
            if (t != null) {
                t.printStackTrace();
            } else {
                System.out.println("AsyncTask Response-2: " + v);
            }
        });
    }


    @RequestMapping("sayHello")
    public String sayHello(String param) {
        return asyncService.sayHello(param);
    }
}

注意:
1、使用CompletableFuture实现异步时,如果接口报错,不会走降级容错
2、业务代码写在try代码块内,报错会被捕获,也不会走降级容错

Consumer异步

@RestController
@RequestMapping("async/prc/")
public class AsyncCustomer {
    @DubboReference(
            version = "0.0.1",
            mock = "com.provider.module.dubbo.async.AsyncMock",
            check = false
    )
    private AsyncService asyncService;
    
    //consumer异步调用
    @RequestMapping("invoke")
    public String invoke(String param) {
        CompletableFuture<String> future3 =  CompletableFuture.supplyAsync(() -> {
            return asyncService.invoke(param);
        });
        future3.whenComplete((v, t) -> {
            if (t != null) {
                t.printStackTrace();
            } else {
                System.out.println("AsyncTask Response-3: " + v);
            }
        });
        return "consumer异步调用成功";
    }
}

四、泛化调用

dubbo版本要3以上

泛化调用
泛化调用(客户端泛化调用)是指在调用方没有服务方提供的 API(SDK)的情况下,对服务方进行调用,并且可以正常拿到调用结果。

使用场景
调用方没有接口及模型类元,知道服务的接口的全限定类名和方法名的情况下,可以通过泛化调用调用对应接口。 比如:实现一个通用的服务测试框架

使用方式

接口定义:

public interface DemoService {
    String sayHello(String name);
}

接口实现1:

@DubboService(version = "0.0.1", group = "group1")
@Slf4j
public class DemoServiceImpl implements DemoService {

    @Override
    public String sayHello(String name) {
        return "Hello " + name;
    }
    
}

客户端调用:

/**
 * @description: 泛化调用
 * @author: zxh
 * @create: 2024-01-11 11:25
 **/
@RestController
@RequestMapping("generic/")
public class GenericCustomer {

    @RequestMapping("getGeneric")
    public String getGeneric(String param) {
        GenericService genericService = buildGenericService("com.provider.module.dubbo.rpc.DemoService","group1","0.0.1");
        //传入需要调用的方法,参数类型列表,参数列表
        Object result = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{param});
        System.out.println("GenericTask Response: " + JSON.toJSONString(result));
        return JSON.toJSONString(result);
    }

    private GenericService buildGenericService(String interfaceClass, String group, String version) {
        ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
        reference.setInterface(interfaceClass);
        reference.setVersion(version);
        //开启泛化调用
        reference.setGeneric("true");
        reference.setTimeout(30000);
        reference.setGroup(group);
        ReferenceCache cache = SimpleReferenceCache.getCache();
        try {
            return cache.get(reference);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }
}

调用结果:
在这里插入图片描述

  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值