Dubbo总结

news2024/12/22 20:54:05

目录

什么是分布式系统

单机架构、集群架构、分布式架构

Dubbo的概念

Dubbo的核心组件

Dubbo的常用注解

Dubbo的高级特性:

        序列化特性安全

        地址缓存

        超时机制

        重试机制

        多版本灰度发布

        负载均衡 

        集群容错

        服务降级

        服务限流

        结果缓存

Dubbo实战:

        项目介绍

        搭建架构

        编写pojo实体类

        编写mapper持久层接口

        编写producer生产者代码

                 编写api模块代码

        编写consumer消费者代码

                引入相关依赖(dubbo、zookeeper、Dubbo_api)

                编写service层代码(这里的@Service就是Spring的注解)

                编写PageController(该控制层的作用就是跳转到对应页面)

                编写UserController

                编写index.html主页

                配置dubbo和zookeeper

                新增用户业务实现

                添加用户测试

                查询用户业务实现

                查询用户测试 

                修改用户业务实现

                修改用户测试 

                删除用户业务实现


 

部分图片来自百战尚学堂 

什么是分布式系统

分布式就是很多“人”一起干不一样的事,合起来就是一件大事,意思就是一个大的业务系统,拆分成一个个小的业务模块,分别部署到不同的机器上

优点:解耦,代码复用性更高,独立部署,独立测试

单机架构、集群架构、分布式架构

单机架构就是一个“人”干所有事,集群架构就是多个“人”干相同的事,分布式架构就是不同的“人”干不同的事,但是合起来就是在干一件大事

单机架构的缺点就是当这个“人”出了一些故障,那么整个系统都将崩溃,代码耦合度较高,复用性不高,测试起来复杂。优点就是体系小,开发快,集群架构也是类似的

分布式架构的优点就是代码耦合度低,没有单点故障问题,一个“人”出错了,并不会影响其他“人”,代码复用性高,测试方便。缺点就是会导致体系变得庞大

Dubbo的概念

Dubbo是一个RPC框架,作用就是让远程调用像本地调用一样简单、方便,通常使用Zookeeper+Dubbo实现分布式系统

什么是RPC:RPC让你用别人家的东西就像自己家的一样。

RPC两个作用:

  • 屏蔽远程调用跟本地调用的区别,让我们感觉就是调用项目内的方法
  • 隐藏底层网络通信的复杂性,让我们更加专注业务逻辑。

常用的RPC框架

RPC是一种技术思想而非一种规范或协议。

常见 RPC 技术和框架:

  • 阿里的 Dubbo/Dubbox、Google gRPC、Spring Cloud。

 

Dubbo的核心组件

注册中心Registry

在Dubbo微服务体系中,注册中心是其核心组件之一。Dubbo通过注册中心实现了分布式环境中各服务之间的注册与发现,是各个分布式节点之间的纽带。

其主要作用如下:

  • 动态加入:一个服务提供者通过注册中心可以动态地把自己暴露给其他消费者,无须消费者逐个去更新配置文件。
  • 动态发现:一个消费者可以动态地感知新的配置、路由规则和新的服务提供者,无须重启服务使之生效。
  • 动态调整:注册中心支持参数的动态调整,新参数自动更新到所有相关服务节点。
  • 统一配置:避免了本地配置导致每个服务的配置不一致问题。

 

服务提供者Provider

服务的提供方

服务消费者Consumer

调用远程服务的服务消费方

监控中心Monitor

主要负责监控统计调用次数和调用时间等。

 

Dubbo的常用注解

@Service、@Reference

@Service:将类注册到注册中心

@Reference:将类从注册中心拉取到本地

Dubbo的高级特性

        高级特性其实就是@Service、@Reference注解的一些属性        

        序列化特性安全

网络传输数据都是以二进制的形式进行传输的,但调用方请求的出入参数都是对象,此时就需要这些对象实现了Serializable方法,即可序列化,这样才能在网络中传输 

        打个比方:就像送快递一样,你的货物就相当于那个对象,货物需要打包相当于对象需要序列化为二进制,当对象到你手上的时候就是一串二进制,就像快递包到了你手上一样,此时就需要拆开快递包即可拿到自己的货物,就相当于反序列化一样将二进制变回对象

 

        地址缓存

注册中心挂了,服务是否可以正常访问?

答案:

因为dubbo服务消费者在第一次调用时会将服务提供方地址缓存到本地以后在调用则不会访问注册中心。服务提供者地址发生变化时,注册中心会通服务消费者。

 

        超时机制

问题:

  • 服务消费者在调用服务提供者的时候发生了阻塞、等待的情形,这个时候,服务消费者会一直等待下去。
  • 在某个峰值时刻,大呈的请求都在同时请求服务消费者,会造成线程的大呈堆积,势必会造成雪崩。
  • dubbo利用超时机制来解决这个问题,设置一个超时时间,在这个时间段内,无法完成服务访问,则自动断开连接。

 

 

服务生产者端配置超时时间

使用timeout属性配置超时时间,默认值1000,单位毫秒。

@Service(timeout = 3000) //当前服务3秒超时

 

消费端配置超时时间

@Reference(timeout=2000)// 远程注入

private IOrderService iOrderService;

 

        重试机制

超时问题:

如果出现网络抖动,则会出现请求失败。

如何解决

Dubbo提供重试机制来避免类似问题的发生。

重试机制配置

@Service(timeout = 3000,retries = 2)

 

        多版本灰度发布

Dubbo提供多版本的配置,方便我们做服务的灰度发布,或者是解决不兼容的问题。

灰度发布(金丝雀发布):

当出现新功能时,会让一部分用户先使用新功能,用户反馈没问题时,再将所有用户迁移到新功能。

版本迁移步骤

  1. 在低压力时间段,先升级一半提供者为新版本
  2. 再将所有消费者升级为新版本
  3. 然后将剩下的一半提供者升级为新版本

多版本配置

老版本服务提供者配置

@Service(version = "1.0.0")        设置版本为1.0

新版本服务提供者配置

@Service(version = "2.0.0")        设置版本为2.0

 

新版本服务消费者配置

@Reference(version="2.0.0")        代表拉取的是2.0版本的该对象

private IOrderService iOrderService;// 订单服务

 

如果不需要区分版本,可以按照以下的方式配置 :

@Reference(version="*")

private IOrderService iOrderService;// 订单服务

 

        负载均衡 

Dubbo是一个分布式服务框架,能避免单点故障和支持服务的横向扩容。一个服务通常会部署多个实例。

问题:

订单服务生产者会出现单点故障。

如何从多个服务 Provider 组成的集群中挑选出一个进行调用,就涉及到一个负载均衡的策略。

Dubbo内置负载均衡策略

  1. RandomLoadBalance:随机负载均衡,随机的选择一个,默认负载均衡。
  2. RoundRobinLoadBalance:轮询负载均衡。
  3. LeastActiveLoadBalance:最少活跃调用数,相同活跃数的随机。
  4. ConsistentHashLoadBalance:一致性哈希负载均衡,相同参数的请求总是落在同一台机器上。

负载均衡策略配置

如果不指定负载均衡,默认使用随机负载均衡。我们也可以根据自己的需要,显式指定一个负载均衡。

生产者服务

@Service(timeout=3000,retries=3,loadbalance="roundrobin")

消费者服务

@Reference(timeout=2000,loadbalance="roundrobin")

 

参数:

  • random:随机负载均衡
  • leastactive:最少活跃调用数,相同活跃数的随机
  • roundrobin:轮询负载均衡
  • consistenthash:一致性哈希负载均衡

        集群容错

Dubbo框架为服务集群容错提供了一系列好的解决方案,在此称为dubbo服务集群容错模式。

容错模式

  • Failover Cluster:失败重试。默认值。当出现失败,重试其它服务器,默认重试2次,使用retries配置。一般用于读操作
  • Failfast Cluster : 快速失败,只发起一次调用,失败立即报错。通常用于写操作。
  • Failsafe Cluster : 失败安全,出现异常时,直接忽略。返回一个空结果。日志不重要操作。
  • Failback Cluster : 失败自动恢复,后台记录失败请求,定时重发。非常重要的操作。
  • Forking Cluster:并行调用多个服务器,只要有一个成功即返回。
  • Broadcast Cluster:广播调用所有提供者,逐个调用,任意一台报错则报错。 同步要求高的可以使用这个模式。

集群容错配置

在消费者服务配置

@Reference(cluster = "failover")

private IOrderService iOrderService;

 

        服务降级

服务降级,当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。

两种场景:

  • 当下游的服务因为某种原因响应过慢,下游服务主动停掉一些不太重要的业务,释放出服务器资源,增加响应速度!
  • 当下游的服务因为某种原因不可用,上游主动调用本地的一些降级逻辑,避免卡顿,迅速返回给用户!

为什么需要降级

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。

 

服务降级方式

第一种

mock=force:return null

含义:

表示消费方对该服务的方法调用都直接返回null值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。

 

第二种

mock=fail:return null

含义:

表示消费方对该服务的方法调用在失败后,再返回null值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。

服务降级演示

@Reference(timeout = 2000,mock = "force:return null")

private IOrderService iOrderService;

 

        服务限流

并发控制

@Service(executes = 10)

注意:

服务端并发执行(或占用线程池线程数)不能超过10个

 

连接控制

@Service(actives= 10)

注意:

占用连接的请求的数不能超过10个。

 

        结果缓存

 

//通过注解中的cache属性配置结果缓存机制

@Reference(cache="lru")

 

Dubbo实战

        项目介绍

需求:完成用户的增删改查操作

技术栈:

        前端:html、thymeleaf

        分布式:Dubbo、Zookeeper、SpringMVC

        持久化:MySql、MyBatisPlus

项目架构:

 

 

 

        搭建架构

1、在Dubbo_father的pom文件中定义需要用的依赖的版本并配置jdk版本,在配置中有一个标签叫做dependencyManagement,这个标签的作用就是声明该依赖的版本,当子模块引入该依赖时,无需定义版本,可以直接使用父项目定义好的版本

<?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.itbaizhan</groupId>
    <artifactId>Dubbo_father</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>Dubbo_producer</module>
        <module>Dubbo_api</module>
        <module>dubbo_consumer</module>
    </modules>

    <properties>
        <dubbo.spring.starter.version>2.7.6</dubbo.spring.starter.version>
        <dubbo.registry.zookeeper.version>2.7.6</dubbo.registry.zookeeper.version>
        <mybatisplus.spring.starter.version>3.5.0</mybatisplus.spring.starter.version>
        <mysql.connector.version>5.1.49</mysql.connector.version>
    </properties>

<!-- 通过management提前声明这些依赖所使用的版本,等子项目使用的时候即可不需要定义版本,直接使用父项目声明的版本   -->
    <dependencyManagement>
        <dependencies>
            <!-- Dubbo 依赖 -->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-spring-boot-starter</artifactId>
                <version>${dubbo.spring.starter.version}</version>
            </dependency>
            <!-- zookeeper 注册中心 依赖 -->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-registry-zookeeper</artifactId>
                <version>${dubbo.registry.zookeeper.version}</version>
            </dependency>
            <!-- Mybatis plus 依赖 -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatisplus.spring.starter.version}</version>
            </dependency>
            <!--MySQL 数据库依赖 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.connector.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

<!--  设置jdk版本  -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
        </plugins>
    </build>



</project>

因为Dubbo_consumer模块是SpringBoot项目,所以他的父项目是SpringBoot的起步依赖spring-boot-stater-parent,所以我们需要添加第二个父项目,通过dependencyManagement标签即可实现

<?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.11</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <!--  引入第二个父亲  -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.itbaizhan</groupId>
                <artifactId>Dubbo_father</artifactId>
                <type>pom</type>
                <version>1.0-SNAPSHOT</version>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <groupId>com.itbaizhan</groupId>
    <artifactId>dubbo_consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>dubbo_consumer</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </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>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

        编写pojo实体类

创建数据库和表

创建数据库
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)
);

创建User实体类(User实体类需要实现Serializable接口,前面有提到过)

package com.itbaizhan.pojo;

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

import java.io.Serializable;

/**
 * 用户实体类
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    public Long id;//用户id
    public String name;//用户名字
    public int age;//用户年龄
}

mapper模块引入pojo模块

 

        编写mapper持久层接口

引入MybatisPlus和mysql依赖

<?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">
    <parent>
        <artifactId>Dubbo_producer</artifactId>
        <groupId>com.itbaizhan</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mapper</artifactId>

    <dependencies>
    <!--   引入pojo     -->
        <dependency>
            <groupId>com.itbaizhan</groupId>
            <artifactId>pojo</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--    MyBatisPlus依赖    -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <!--    MySql依赖    -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>
</project>

 编写UserMapper

package com.itbaizhan.mapper;

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

public interface UserMapper extends BaseMapper<User> {
}

 

        编写producer生产者代码

将producer修改为SpringBoot项目,因为SpringBoot项目的父类是spring-boot-stater-parent,而本身项目的父类是dubbo_producer,所以需要通过dependencyManagement引入第二个父项目

<?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">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.11</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencyManagement>
        <dependencies>
            <!--     引入第二个父项目       -->
            <dependency>
                <artifactId>Dubbo_producer</artifactId>
                <groupId>com.itbaizhan</groupId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>producer</artifactId>


</project>

引入mapper模块,通过依赖传递的方式引入pojo模块

<dependencies>
        <dependency>
            <groupId>com.itbaizhan</groupId>
            <artifactId>mapper</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

 

 编写启动类并扫描持久层接口创建相应的实现类放到spring容器中

 

package com.itbaizhan.producer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

 配置数据源

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://192.168.138.101/test
    username: root
    password: 123456

        编写api模块代码

添加pojo模块依赖

<?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">
    <parent>
        <artifactId>Dubbo_father</artifactId>
        <groupId>com.itbaizhan</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>Dubbo_api</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.itbaizhan</groupId>
            <artifactId>pojo</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>


</project>

编写api模块service层接口(该接口的作用是定义producer模块的service规范)

package com.itbaizhan.service;

import com.itbaizhan.pojo.User;

import java.util.List;

public interface UserService {
    //新增用户
    public void add(User user);
    
    //根据id删除用户
    public void delete(Long userId);
    
    //根据id修改用户
    public void update(User user);
    
    //查询所有用户
    public List<User> selectAll();
    //根据id查询用户
    public User selectById();
    
}

producer模块引入相关依赖

<?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">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.11</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencyManagement>
        <dependencies>
            <!--     引入第二个父项目       -->
            <dependency>
                <groupId>com.itbaizhan</groupId>
                <artifactId>Dubbo_father</artifactId>
                <type>pom</type>
                <version>1.0-SNAPSHOT</version>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <modelVersion>4.0.0</modelVersion>

    <artifactId>producer</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.itbaizhan</groupId>
            <artifactId>mapper</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--     引入dubbo       -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
        </dependency>
        <!--   引入zookeeper     -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-registry-zookeeper</artifactId>
        </dependency>
        <!--   引入dubbo_api模块     -->
        <dependency>
            <groupId>com.itbaizhan</groupId>
            <artifactId>Dubbo_api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

编写producer模块service层代码(类的上方需要添加Dubbo的@Service注解,将该类注册到注册中心)

package com.itbaizhan.producer.service;

import com.itbaizhan.mapper.UserMapper;
import com.itbaizhan.pojo.User;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

@Service
public class UserService implements com.itbaizhan.service.UserService {
    
    @Autowired
    private UserMapper userMapper;

    /**
     * 添加用户
     * @param user
     */
    @Override
    public void add(User user) {
        userMapper.insert(user);
    }

    /**
     * 根据id删除用户
     * @param userId
     */
    @Override
    public void delete(Long userId) {
        userMapper.deleteById(userId);
    }

    /**
     * 根据id修改用户
     * @param user
     */
    @Override
    public void update(User user) {
        userMapper.updateById(user);
    }

    /**
     * 查询所有用户
     * @return
     */
    @Override
    public List<User> selectAll() {
        return userMapper.selectList(null);
    }

    /**
     * 根据id查询用户
     * @param userId
     * @return
     */
    @Override
    public User selectById(Long userId) {
        return userMapper.selectById(userId);
    }
}

 配置dubbo和zookeeper

dubbo:
  #项目名字
  application:
    name: myProducer
  #注册中心地址
  registry:
    address: zookeeper://192.168.138.101:2181
    timeout: 50000
  #端口号和协议名
  protocol:
    port: 20880
    name: dubbo
  #扫描的包
  scan:
    base-packages: com.itbaizhan.producer.service

运行producer模块启动类,通过dubbo-admin查看是否注册到了注册中心

 

        编写consumer消费者代码

                引入相关依赖(dubbo、zookeeper、Dubbo_api)

<!--     引入dubbo       -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
        </dependency>
        <!--   引入zookeeper     -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-registry-zookeeper</artifactId>
        </dependency>
        <!--   引入dubbo_api模块     -->
        <dependency>
            <groupId>com.itbaizhan</groupId>
            <artifactId>Dubbo_api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

                编写service层代码(这里的@Service就是Spring的注解)

通过@Reference拉取Dubbo_api模块的UserService,那么此时会有小伙伴问了:我们注册的是producer模块的UserService,为什么拉取的确实Dubbo_api模块的UserService呢?

这是因为采用了jdk动态代理的模式,就是拉取这个接口的实现类,以接口引用的方式实现调用

package com.itbaizhan.dubbo_consumer.service;

import com.itbaizhan.pojo.User;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
    @Reference
    private com.itbaizhan.service.UserService userService;

    //新增用户
    public void add(User user){
        userService.add(user);
    }

    //根据id删除用户
    public void delete(Long userId){
        userService.delete(userId);
    }

    //根据id修改用户
    public void update(User user){
        userService.update(user);
    }

    //查询所有用户
    public List<User> selectAll(){
        return userService.selectAll();
    }

    //根据id查询用户
    public User selectById(Long userId){
        return userService.selectById(userId);
    }
}

                 编写PageController(该控制层的作用就是跳转到对应页面)

package com.itbaizhan.dubbo_consumer.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class PageController {

    @RequestMapping("/{page}")
    public String page(@PathVariable String page){
        return page;
    }
}

                编写UserController

package com.itbaizhan.dubbo_consumer.controller;

import com.itbaizhan.dubbo_consumer.service.UserService;
import com.itbaizhan.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@Controller
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    //新增用户
    @PostMapping("/add")
    public String add(User user){
        userService.add(user);
        return "redirect:/ok";
    }

    //根据id删除用户
    @GetMapping("/delete")
    public String delete(Long userId){
        userService.delete(userId);
        return "redirect:/ok";
    }

    //根据id修改用户
    @GetMapping("/preUpdate")
    public String preUpdate(Long userId, Model model){
        User user = userService.selectById(userId);
        model.addAttribute("user",user);
        return "update";
    }

    //根据id查询用户
    @PostMapping("/update")
    public String update(User user){
        userService.update(user);
        return "redirect:/ok";
    }

    //查询所有用户
    @GetMapping("/selectAll")
    public String selectAll(Model model){
        List<User> userList = userService.selectAll();
        model.addAttribute("userList",userList);
        return "showuser";
    }

}

                编写index.html主页

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>主页</title>
</head>
<body>
    <a href="/adduser">添加用户</a>
    <a href="/user/selectAll">查询用户</a>
</body>
</html>

                配置dubbo和zookeeper

dubbo:
  #项目名字
  application:
    name: myConsumer
  #注册中心地址
  registry:
    address: zookeeper://192.168.138.101:2181
    timeout: 50000
  #端口号和协议名
  protocol:
    port: 20881
    name: dubbo
  #扫描的包
  scan:
    base-packages: com.itbaizhan.dubbo_consumer.service

运行producer模块和consumer模块,访问localhost:8080/index

此时发现整个项目可以正常运行,那么我们就可以继续编写页面了

                新增用户业务实现

编写adduser.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加用户</title>
</head>
<body>
    <form action="/user/add" method="post">
        用户名:<input type="text" name="name">&nbsp;&nbsp;&nbsp;
        年龄:<input type="text" name="age"><br/>
        <input type="submit" value="提交">
    </form>

</body>
</html>

编写ok.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>操作成功</title>
</head>
<body>
    操作成功,点击<a href="/index.html">返回首页</a>
</body>
</html>

                添加用户测试:

点击添加用户 

点击提交

 查看是否添加用户成功

测试成功之后编写查询所有用户(需要用到thymeleaf)

                查询用户业务实现

编写showuser.html对用户有两个操作,修改和删除

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>用户列表</title>
</head>
<body>
    <table border="1px solid black" align="center">
        <tr>
            <th>id</th>
            <th>用户姓名</th>
            <th>用户年龄</th>
            <th>操作</th>
        </tr>
        <tr th:each="user:${userList}">
            <td th:text="${user.id}"></td>
            <td th:text="${user.name}"></td>
            <td th:text="${user.age}"></td>
            <td>
                <a th:href="@{/user/preUpdate(userId=${user.id})}">修改</a>&nbsp;
                <a th:href="@{/user/delete(userId=${user.id})}">删除</a>
            </td>
        </tr>
    </table>
</body>
</html>

                查询用户测试 

点击查询用户 

 

 

                修改用户业务实现

编写update.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">
        <!--  userId的隐藏域      -->
        <input type="hidden" name="id" th:value="${user.id}">
        用户名:<input type="text" name="name" th:value="${user.name}">&nbsp;&nbsp;&nbsp;
        年龄:<input type="text" name="age" th:value="${user.age}"><br/>
        <input type="submit" value="修改">
    </form>
</body>
</html>

                修改用户测试 

点击修改用户 

 

修改用户数据,点击修改按钮过后跳转到操作成功页面,回到首页再次查询用户会发现,用户数据已经更改

                删除用户业务实现

删除用户的业务在实现查询用户业务的时候已经写好了,只要点击删除即可删除用户

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

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

相关文章

40.java-Set集合(HashSet,LinkedHashSet,TreeSet)

Set集合 1.Set集合特点2.Set集合实现类3. HashSet3.1 底层原理3.1.1 哈希表组成3.1.2 哈希值3.1.3 对象的哈希值特点 3.2 数据添加元素的过程3.3 HashSet的三个问题3.4 实例&#xff1a;去除重复元素 4. LinkedHashSet5. TreeSet5.1 特点5.2 集合默认规则5.3 例子5.4 两种比较规…

JavaScript 知识总结上篇(更新版)

1. 为什么 JS 是单线程的&#xff1f; 因为JS里面有可视的Dom&#xff0c;如果是多线程&#xff0c;这个线程正在删除DOM节点&#xff0c;另一个线程正在编辑Dom节点&#xff0c;导致浏览器不知道该听谁的 2.如何理解同步和异步&#xff1f; 同步&#xff1a;按照代码书写顺…

Linux——中断和时间管理(下)

目录 延时控制 定时操作 低分辨率定时器 高分辨率定时器 练习 延时控制 在硬件的操作中经常会用到延时&#xff0c;比如要保持芯片的复位时间持续多久、芯片复位后要至少延时多长时间才能去访问芯片、芯片的上电时序控制等。为此&#xff0c;内核提供了一组延时操作函数。…

DDD系列:三、Repository模式

为什么需要Repository&#xff1f; ​ Anemic Domain Model&#xff08;贫血领域模型&#xff09;特征&#xff1a; 有大量的XxxDO对象&#xff1a;这里DO虽然有时候代表了Domain Object&#xff0c;但实际上仅仅是数据库表结构的映射&#xff0c;里面没有包含&#xff08;或…

kafka整理

kafka整理 一、kafka概述 kafka是apache旗下一款开源的顶级的消息队列的系统, 最早是来源于领英, 后期将其贡献给apache, 采用语言是scala.基于zookeeper, 启动kafka集群需要先启动zookeeper集群, 同时在zookeeper记录kafka相关的元数据 kafka本质上就是消息队列的中间件产品…

Codeforces Round 867 (Div. 3)(A-G2)

文章目录 A. TubeTube Feed1、题目2、分析3、代码&#xff0c; B. Karina and Array1、题目2、分析3、代码 C. Bun Lover1、问题2、分析&#xff08;1&#xff09;观察样例法&#xff08;2&#xff09;正解推导 3、代码 D. Super-Permutation1、问题2、分析&#xff08;1&#…

力扣第343场周赛

第一次力扣&#xff0c;等大二寒暑假&#xff0c;有时间再来系统刷题 目录 &#x1f33c;前言 &#x1f33c;一&#xff0c;6341.保龄球游戏的获胜者 &#x1f33c;二&#xff0c;6342.找出叠涂元素 &#x1f333;第一次 -- 超时 &#x1f333;第二次 -- AC &#x1f33c…

二叉树相关的简单递归oj

二叉树相关的简单递归oj 前言题目二叉树的前序遍历相同的树判断单值二叉树对称二叉树另一棵树的子树创建二叉树并遍历 前言 这篇博客主要是博主感觉对二叉树oj题目不太熟悉&#xff0c;随便整理的一下题目和解答&#xff0c;方便复习&#xff0c;所以讲题部分主要以我自己以及为…

Java 基础入门篇(二)——— Java 基础语法

文章目录 一、注释二、字面量三、变量3.1 变量概述3.2 变量在计算机中的底层原理 四、数据类型五、关键字、标志符六、类型转换6.1 自动类型转换6.2 表达式的自动类型转换6.3 强制类型转换 七、运算符7.1 基本算数运算符7.2 符号做连接符7.3 自增自减运算符7.4 赋值运算符7.5 …

【C++技能树】类的六个成员函数Ⅰ --构造、析构、拷贝构造函数

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法…感兴趣就关注我吧&#xff01;你定不会失望。 本篇导航 0.this指针1.Class默认成员函数2.构造函数调用规则: 3.析构函数4.拷贝构造函数 0.this指针 在开始本章内容之前&#xff0c;先浅…

Channel-wise Knowledge Distillation for Dense Prediction(ICCV 2021)原理与代码解析

paper&#xff1a;Channel-wise Knowledge Distillation for Dense Prediction official implementation&#xff1a;https://github.com/irfanICMLL/TorchDistiller/tree/main/SemSeg-distill 摘要 之前大多数用于密集预测dense prediction任务的蒸馏方法在空间域spatial…

(求正数数组的最小不可组成和,养兔子)笔试强训

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE初阶 目录 文章目录 一、选择题1 二、[编程题]养兔子 三、[编程题]求正数数组的最小不可组成和 一、选择题1 reflection是如何工作的__牛客网 (nowcoder.com) 考虑下面这个简单的例子&…

大数据Doris(八):Broker部署和集群启停脚本

文章目录 Broker部署和集群启停脚本 一、Broker部署 1、准备Broker 安装包 2、启动 Broker

PyQt6剑指未来-日期和时间

前言 时间和日期是软件开发中非常重要的概念。在PyQt6中&#xff0c;时间和日期模块提供了处理日期、时间和日期时间的类和函数&#xff0c;以及管理时区和夏令时的特性。这些模块提供了可靠和易于使用的工具&#xff0c;使得在PyQt6中处理和呈现时间和日期的操作变得轻松起来…

Java中Lambda表达式(初学到精通)

目录 一、Lambda表达式是什么&#xff1f;什么场景下使用Lambda&#xff1f; 1.Lambda 表达式是什么 2.函数式接口是什么 第二章、怎么用Lambda 1.必须有一个函数式接口 2.省略规则 3.Lambda经常用来和匿名内部类比较 第三章、具体使用举例&#xff08;&#xff09; 1.案…

跳跃游戏类题目 总结篇

一.跳跃游戏类题目简单介绍 跳跃游戏是一种典型的算法题目&#xff0c;经常是给定一数组arr&#xff0c;从数组的某一位置i出发&#xff0c;根据一定的跳跃规则&#xff0c;比如从i位置能跳arr[i]步&#xff0c;或者小于arr[i]步&#xff0c;或者固定步数&#xff0c;直到到达某…

C++ 链表概述

背景 当需要存储大量数据并需要对其进行操作时&#xff0c;常常需要使用到链表这种数据结构。它可以用来存储一系列的元素并支持插入、删除、遍历等操作。 概念 一般来说&#xff0c;链表是由若干个节点组成的&#xff0c;每个节点包含了两个部分的内容&#xff1a;存储的数…

【嵌入式环境下linux内核及驱动学习笔记-(6-内核 I/O)-阻塞与非阻塞】

目录 1、阻塞与非阻塞1.1 以对recvfrom函数的调用及执行过程来说明阻塞的操作。1.2 以对recvfrom函数的不断轮询调用为例&#xff0c;说明非阻塞时进程的行为。1.3 简单介绍内核链表及等待队列1.4 等待队列1.4.1 定义等待队列头部&#xff08;wait_queue_head_t&#xff09;1.4…

vue动态添加多组数据添加正则限制

如图新增多条数据&#xff0c;如果删除其中一条正则校验失败的数据&#xff0c;提示不会随之删除&#xff0c;若想提示删除并不清空数据 delete (item, index) {this.applicationForm.reserveInfo.forEach((v, i) > {if (i index) {this.$refs.formValidate.fields.forEac…