Dubbo总结

目录

什么是分布式系统

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

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>

                修改用户测试 

点击修改用户 

 

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

                删除用户业务实现

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值