当前位置: 首页 > article >正文

SpringCloudAlibaba技术栈-Dubbo

1、什么是Dubbo?

简单来说,dubbo就像是个看不见的手,负责专门从注册中心nacos调用注册到nacos上面的服务的,因为在微服务环境下不同的功能模块可能在不同的服务器上。dubbo调用服务就像是在调用本地的服务一样。

分布式调用与高并发处理 Dubbo分布式调用_分布式之间的调用-CSDN博客

2、Dubbo实现

(1)创建父项目

添加相关依赖

<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.zj</groupId>
    <artifactId>Dubbo_demo2</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>Dubbo_demo2</name>
    <url>http://maven.apache.org</url>

    <properties>
        <dubbo.version>3.2.4</dubbo.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>17</java.version>
        <spring-boot.version>3.0.2</spring-boot.version>
        <spring-cloud.version>2022.0.0</spring-cloud.version>
        <spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
        <lombok.version>1.8.28</lombok.version>
    </properties>


    <dependencyManagement>
        <dependencies>


            <!-- SpringCloud 微服务 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>


            <!-- SpringCloud Alibaba 微服务 -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>


            <!-- SpringBoot 依赖配置 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- lombok 依赖配置 -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}}</version>
            </dependency>


            <!-- Dubbo -->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-bom</artifactId>
                <version>${dubbo.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>


            <!-- bootstrap 启动器 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-bootstrap</artifactId>
            </dependency>


            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>3.8.1</version>
                <scope>test</scope>
            </dependency>

        </dependencies>

    </dependencyManagement>

</project>

(2)创建子模块interface

添加下面的依赖文件,为啥要添加mybatis依赖呢?因为有些时候需要指定实体类对应的表和实体类和表字段之间的对应关系等。lombok就是为了生成实体类的get\set等方法。

        <!-- Mybatis plus 依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.0</version>
        </dependency>

       <!--lombok依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.28</version>
        </dependency>

在test数据库创建User表

create database test;
CREATE TABLE user
 (
    id 
BIGINT(20) NOT NULL COMMENT '主键ID',
    name VARCHAR(30) NULL DEFAULT NULL COMMENT 
'姓名',
    age INT(11) NULL DEFAULT NULL COMMENT '年
龄',
 PRIMARY KEY (id)
 );

在interface下创建User实体类

@TableName("user")
@Data
public class User {
    private Long id;
    private String name;
    private Integer age;
}

创建结果集

package com.zj.common;

import java.io.Serializable;

/*结果集*/
/*实现序列化因为dubbo传输的是二进制数据*/
public class CommonResult<T> implements Serializable {

private Integer code;
private String msg;
private T data;

}

创建IUserService接口

package com.zj.service;

import com.zj.common.CommonResult;
import com.zj.pojo.User;

/*用户接口*/
public interface IUserService {

    CommonResult<User> createUser(User user);
    CommonResult<User> findAllUser(User user);
    CommonResult<User> updateUser(User user);
    CommonResult<User> deleteUser(Long id);


}

(3)创建子模块user-service

这个模块就是对用户业务的具体实现,这里面的业务是需要注册到nacos上面的。

添加依赖文件

    <dependencies>

        <!--springboot依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--dubbo整合spring boot的依赖包-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
        </dependency>

        <!--dubbo注册到nacos上的依赖包-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-registry-nacos</artifactId>
        </dependency>


        <!-- Mybatis plus 依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.7</version>
        </dependency>

        <!--MySQL 数据库依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>

        <!--接口的依赖-->
        <dependency>
            <groupId>com.zj</groupId>
            <artifactId>1interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

    </dependencies>

创建mapper层获取数据库的数据。

package com.zj.mapepr;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zj.pojo.User;

public interface UserMapper extends BaseMapper<User> {
    
}

别忘了在启动类上添加上这三个注解。

@EnableDubbo
@MapperScan("com.zj.mapper")
@SpringBootApplication
public class userService
{
    public static void main( String[] args )
    {
        SpringApplication.run(userService.class, args);
    }
}

 @EnableDubbo注解是使用Dubbo进行服务化开发的关键注解,它使得Spring Boot应用能够方便地集成和使用Dubbo框架提供的分布式服务能力。

当你将这个注解添加到你的Spring Boot应用的配置类上时,它会做以下几件事情:

  1. 开启Dubbo自动配置:它会触发Spring Boot的自动配置机制,自动配置Dubbo相关的Bean。

  2. 服务暴露:在Spring容器中,标注了@Service(注意这里不是Spring的@Service,而是Dubbo的@Service注解)的类会被识别为Dubbo服务,并且会被注册到注册中心(比如Zookeeper),从而可以被其他服务发现和调用。

  3. 服务引用:它允许你的应用通过Dubbo去引用其他服务。通常是通过@Reference注解来注入其他Dubbo服务。

  4. 配置加载:它会加载Dubbo相关的配置,这些配置可以是写在application.propertiesapplication.yml文件中,也可以是通过其他方式定义的Dubbo配置类。

  5. 依赖注入:它支持将Dubbo的Reference(服务引用)注入到Spring管理的Bean中,使得远程服务调用就像调用本地方法一样简单。

  6. 服务监控@EnableDubbo还负责集成Dubbo的监控功能,比如可以通过配置将服务的调用次数、调用时间等信息发送到监控中心。

创建service层实现IUserService接口,并将该业务注册到nacos上。

package com.zj.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.zj.common.CommonResult;
import com.zj.mapper.UserMapper;
import com.zj.pojo.User;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;


import java.util.List;

/*用户的业务层*/
@DubboService   //将该业务发布到注册中心nacos
public class UserServiceImpl implements IUserService{

    @Autowired
    private UserMapper userMapper;

    /*添加用户*/
    @Override
    public CommonResult<User> createUser(User user) {
        CommonResult<User> userCommonResult = new CommonResult<>();
        int insert = userMapper.insert(user);
        if(insert > 0){
            userCommonResult.setCode(200);  //结果的编码
            userCommonResult.setMsg("success");  //结果的描述
        }else {
            userCommonResult.setCode(500);
            userCommonResult.setMsg("fail");
        }
        return userCommonResult;
    }


    /*查询用户,这个地方不能加泛型*/
    @Override
    public CommonResult findAllUser(User user) {
        CommonResult userCommonResult = new CommonResult<>();
        //查询条件构造器
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        //id不为空的话使用id查询
        lqw.eq(user.getId() != null,User::getId,user.getId());
        //name不为空的话使用id查询
        lqw.eq(user.getName() != null,User::getName,user.getName());
        //age不为空的话使用id查询
        lqw.eq(user.getAge() != null,User::getAge,user.getAge());

        //查询用户
        List<User> users = userMapper.selectList(lqw);
        userCommonResult.setCode(200);
        userCommonResult.setMsg("success");
        userCommonResult.setData(users);
        return userCommonResult;
    }


    //更新用户
    @Override
    public CommonResult updateUser(User user) {
        CommonResult userCommonResult = new CommonResult<>();
        if(user.getId() == null){
            userCommonResult.setCode(500);
            userCommonResult.setMsg("id = null");
            return userCommonResult;
        }

        /*条件构造器*/
        LambdaUpdateWrapper<User> lqw = new LambdaUpdateWrapper<>();
        lqw.set(user.getName() != null, User::getName, user.getName())
                .set(user.getAge() != null, User::getAge, user.getAge())
                .eq(User::getId, user.getId());

        //更新
        int update = userMapper.update(null, lqw);
        if(update > 0){
            userCommonResult.setCode(200);
            userCommonResult.setMsg("success");
        }else {
            userCommonResult.setCode(500);
            userCommonResult.setMsg("fail");
        }
        return userCommonResult;
    }


    //删除
    @Override
    public CommonResult<User> deleteUser(Long idr) {
        CommonResult<User> userCommonResult = new CommonResult<>();
        if(idr == null){
            userCommonResult.setCode(500);
            userCommonResult.setMsg("id = null");
            return userCommonResult;
        }
        int i = userMapper.deleteById(idr);
        if(i > 0){
            userCommonResult.setCode(200);
            userCommonResult.setMsg("success");
        }else {
            userCommonResult.setCode(500);
            userCommonResult.setMsg("fail");
        }
        return userCommonResult;
    }

}

需要注意的是查询全部的用户的时候不能写泛型,因为查询出来的不是User而是个List。 

创建user-service模块的配置文件application.yml文件

dubbo:
  application:
#    项目名称
    name: user-service
#    通讯协议
  protocol:
    name: dubbo
#    端口号 设置端口为 -1 表示 dubbo 自动扫描并使用可用端口(从20880开始递增),避免了端口冲突的问题。
    port: -1
  registry:
#    服务的注册地址
    address: nacos://192.168.66.100:8848

server:
  port: 8001

#配置数据源
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://192.168.66.100:3306/test?serverTimezone=UTC
    username: root
    password: 123456

 (4)服务接口测试

启动user-service模块

创建测试项目,使apifox测试,选择Dubbo类型的项目。

 导入接口数据,注意导入的是nacos的数据。

(5)创建消费者模块

导入消费者模块的依赖

 <dependencies>

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

        <!--springboot依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--dubbo整合spring boot的依赖包-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
        </dependency>

        <!--dubbo注册到nacos上的依赖包-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-registry-nacos</artifactId>
        </dependency>

        <!--这个不能忘记,因为在消费者模块中也就是consume中需要使用userService模块的服务-->
        <dependency>
            <groupId>com.zj</groupId>
            <artifactId>1interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

添加配置文件。

消费者也是需要调用nacos中的服务的因此也需要将服务注册到nacos上。因此需要添加下面的配置,除此之外还需要添加Thymeleaf的配置。

spring-boot很多配置都有默认配置,比如默认页面映射路径为:classpath:/templates/*.html

同样静态文件路径为:classpath:/static/
 

thymeleaf是前端的页面因此这里创建子模块是consume是专门消费服务的,也就是前台发请求显示页面的模块。

在consume 模块的resource目录下创建template文件夹和static文件夹。

dubbo:
  application:
    name: consume-service
  protocol:
    name: dubbo
  registry:
    address: nacos://192.168.66.100:8848
server:
  port: 8002

spring:
  thymeleaf:
    cache: false
    mode: HTML5
    encoding: utf-8
    content-type: text/html

在templates下面创建视图页面。

首页

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>

<a href="/addUser">添加用户</a>
<a href="/user/showUser">查询用户</a>

</body>
</html>

添加用户

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加用户</title>
</head>
<body>

<form action="/user/addUser" method="post">
    <input type="hidden" name="id" value="0">
    用户名字:<input type="text" name="name" placeholder="请输入名字">
    用户年龄:<input type="text" name="age" placeholder="请输入年龄">
    <input type="submit" value="添加用户">
</form>

</body>
</html>>

OK页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<link rel="shortcut icon" href="../resources/favicon.ico" th:href="@{/static/favicon.ico}"/>
<head>
    <meta charset="UTF-8">
    <title>成功页面</title>
</head>
<body>
操作成功请<a href="/index">返回</a>
</body>
</html>

error页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
执行失败<a href="/index">返回</a>
</body>
</html>

显示用户信息

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>查询用户</title>
</head>
<body>

<table>
    <tr>
        <td>序号</td>
        <td>名字</td>
        <td>用户年龄</td>
        <td>操作</td>
    </tr>
    <tr th:each="u : ${users}">
        <td th:text="${u.id}"></td>
        <td th:text="${u.name}"></td>
        <td th:text="${u.age}"></td>
        <td>
            <a th:href="@{/user/delete(id=${u.id})}">删除</a>
            <a th:href="@{/user/toUpdate(id=${u.id})}">更新</a>
        </td>
    </tr>
</table>


</body>
</html>

更新用户页面。

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>更新用户</title>
</head>
<body>


<form action="/user/update" method="post">
    <input type="hidden" name="id" th:value="${user.id}">
    用户名字:<input type="text" name="name" placeholder="请输入名字" th:value="${user.name}">
    用户年龄:<input type="text" name="age" placeholder="请输入年龄" th:value="${user.age}">
    <input type="submit" value="更新用户">
</form>
</body>
</html>

然后创建两个controller,一个负责页面跳转的。

package com.zj.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;

/*视图层控制器*/
@Controller
public class IndexController {

    /*页面跳转*/
    @GetMapping("/{page}")
    public String index(@PathVariable String page) {
        return page;
    }
    
    /*忽略favicon*/
    @GetMapping("favicon.ico")
    @ResponseBody
    void noFavicon() {
    }

}

 一个负责处理具体的业务请求的。

package com.zj.controller;


import com.zj.common.CommonResult;
import com.zj.pojo.User;
import com.zj.service.IUserService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;


@Controller
@RequestMapping("/user")
public class UserController {

    //远程调用添加用户的操作,添加用户的服务是user-service模块提供的,需要注意的是要引入interface模块依赖
    @DubboReference
    private IUserService userService;

    /*添加用户*/
    @PostMapping("/addUser")
    public String addUser(User user){
        CommonResult<User> result = userService.createUser(user);
        //判断是不是添加成功
        if (result.getCode() == 200){
            return "redirect:/ok";
        }else {
            return "redirect:/error";
        }
    }

    /*查询用户*/
    @GetMapping("/showUser")
    public ModelAndView selectUser(){
        //既要返回视图还需要返回结果
        ModelAndView modelAndView = new ModelAndView();
        User user1 = new User();
        CommonResult<User> allUser = userService.findAllUser(user1);
        //视图添加数据
        modelAndView.addObject("users",allUser.getData());
        //返回视图
        modelAndView.setViewName("showUser");
        return modelAndView;
    }

    /*根据id查询数据跳转到更新数据的页面*/
    @GetMapping("/toUpdate")
    public ModelAndView toUpdateUser(Long id){
        ModelAndView modelAndView = new ModelAndView();
        User user = new User();
        user.setId(id);
        CommonResult allUser = userService.findAllUser(user);
        List<User> data = (List<User>)allUser.getData();
        if (data.size() > 0){
           modelAndView.addObject("user",data.get(0));  //取出第一个数据
             modelAndView.setViewName("update");
        }
        return modelAndView;
    }

    /*更新用户*/
    @PostMapping("/update")
    public String updateUser(User user){
        System.out.println("user:"+user);
        CommonResult<User> userCommonResult = userService.updateUser(user);
        if (userCommonResult.getCode() == 200){
            return "redirect:/ok";
        }else{
            return "redirect:/error";
        }
    }

    /*删除用户*/
    @GetMapping("/delete")
    public String deleteUser(Long id){
        CommonResult<User> userCommonResult = userService.deleteUser(id);
        if (userCommonResult.getCode() == 200){
            return "redirect:/ok";
        }else {
            return "redirect:/error";
        }
    }
}

需要注意的就是这个@DubboReference注解,就是获取nacos的服务实例,注入到userService变量中。

另外根据id查询用户的时候需要注意的是查询结果是个List所以取出第一个来。

最后启动类加注解。

/*告诉 Spring Boot 不要自动配置数据源。使用我配置的数据源。*/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableDubbo
public class userConsume
{
    public static void main( String[] args )
    {
        SpringApplication.run(userConsume.class, args);
    }
}

 @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})第一次碰到这个注解,不叫的话会出错。

最后是配置文件。

dubbo:
  application:
    name: consume-service
  protocol:
    name: dubbo
  registry:
    address: nacos://192.168.66.100:8848
server:
  port: 8002


#配置视图
spring:
  thymeleaf:
    cache: false
    mode: HTML5
    encoding: utf-8
    content-type: text/html

最后启动user-service模块和user-consum模块,在浏览器输入locahost:8082/index就行啦。

详细的项目代码已经上传。


http://www.kler.cn/a/453445.html

相关文章:

  • kubernetes Gateway API-部署和基础配置
  • 【gulp】gulp 的基本使用
  • 从数据仓库到数据中台再到数据飞轮:电信行业的数据技术进化史
  • 质数生成函数、质数判断备份
  • <论文>语言模型可以进行无监督的多任务学习?
  • 从源码到应用:在线问诊系统与医疗陪诊APP的开发全过程详解
  • 12.26 学习卷积神经网路(CNN)
  • npm淘宝镜像
  • Dilateformer实战:使用Dilateformer实现图像分类任务(二)
  • BLE core 内容整理解释
  • FFMPEG结构体分析
  • Linux高并发服务器开发 第六天(rwx 对于目录和文件的区别 gcc编译器 动态库静态库)
  • yolov4算法及其改进
  • C#异步1
  • 蚂蚁集团 CTO 线大规模调整、多个 AI 业务部门被合并
  • 工业大数据分析算法实战-day16
  • 天池工业蒸汽量预测教程
  • FTT变换Matlab代码解释及应用场景
  • go window安装protoc protoc生成protobuf文件
  • vue关闭eslint校验及开启debugger