79-Dubbo介绍

Dubbo介绍

什么是分布式系统:
分布式系统原理与范型:
定义:
分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统
分布式系统(distributed system)是建立在网络之上的软件系统
简单来说:多个(不同职责)人共同来完成一件事
任何一台服务器都无法满足淘宝的双十一的数据吞吐量,一定是很多台服务器公共来完成的
歇后语:“三个臭皮匠赛过诸葛亮”,就是分布式系统的真实写照
接下来说明一下应用架构的演变,也就是我们代码的操作,但不是数据的操作(后面的Redis的数据库架构演变会说明)
注意:下面的说明比较啰嗦,是为了演变而说明的,所以可以跳过,看这个简单的说明:
简单的说明:
单一:电脑放一个项目
垂直:电脑放页面,电脑放业务,电脑放其他(如mysql)
RPC:电脑放页面,电脑放业务访问,电脑放业务,电脑放其他
电脑可以是多个(包括集群的概念)或者一个
单一应用架构:
当网站流量很小时,只需要一个应用,将所有的功能部署到一起(所有业务都放在一个tomcat里)
从而减少部署节点(也就是服务器)和成本
此时,用于简化增删改查工作量的数据访问框架 (ORM)是关键;
例如:某个超市的收银系统,某个公司的员工管理系统

在这里插入图片描述

ORM:对象关系映射(Object Relational Mapping),在61章博客里也说明过
优点:
小项目开发快 成本低
架构简单
易于测试
易于部署
缺点:
大项目模块耦合严重 不易开发 维护 沟通成本高
新增业务困难
核心业务与边缘业务混合在一块,出现问题互相影响
实际情况理解:我们一般将所有的代码放在一个服务器上,假设访问量变大,我们一般添加服务器(节点),进行集群来解决
但是一般访问的量,在对应的模块,或者系统上是不同的,一些多的访问,可能使得对应的访问变慢,即我们虽然可以添加对应的服务器,但是这是非常浪费的,因为其他的业务并没有那么多访问量
浪费了很多不必要的空间,而为了节省这样的操作(小的服务器,就可以替代),我们就让对应的模块或者系统变成一个服务器
于是就出现了垂直应用架构
垂直应用架构:
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小(或者说提升的效率,又或者说空间的利用率)
将应用拆成几个互不相干的几个应用,以提高效率
大模块按照mvc分层模式,进行拆分成多个互不相关的小模块
并且每个小模块都有独立的服务器,当然,这是好的拆分,实际上可能只是对应代码进行垂直,但任然在同一个服务器里面
实际上这也算是一个垂直,但实际上却是单一架构的,虽然代码在自己身上,但缺点是单一架构的缺点,如添加服务器浪费很多空间
如下面就分成了三个服务器,一个视图层的服务器,一个业务层的服务器,一个数据库层的服务器,所有说是mvc分层
实际上可以分很多层,但大体都是这一类的服务器,下面就是图片的演示
此时,用于加速前端页面开发的web框架(MVC)是关键,因为每个小应用都有独立的页面

在这里插入图片描述

MVC:模型视图控制器 (Model View Controller)
缺点:
模块之间不可能完全没有交集,即一个模块里面有很多个管理服务
假如出现了交集(同样的管理服务),举个例子,不同的模块之间可能都会有用户服务(也可以说是用户管理)
虽然你也可以放在自己身上,或者访问其他服务器的模块的用户服务,服务器里的模块里的服务
但无论是放在其他服务器模块里(死机就访问不了了,比单独死机的更加严重,因为会影响其他业务),还是自己的(重复),都是有缺点的
如我们操作用户服务,可能都会有需要用户模块
即公用模块无法单独重复利用(不是服务器之间的方法,死机就访问不了了)
实际情况理解:根据上面的单一应用架构的实际情况理解开始
虽然我们解决了实现局部的节点空间利用,但是如果这样的模块或者系统越来越多时
可以发现,他们之间,可能都会用到每个模块或者系统的一个操作,即服务(这里是服务,服务也可以说成模块)
如查询所有用户,或者查询姓名的回显等等(这只是某些例子),即会出现同样的代码(相同服务)
而这些同样的代码,由于他们是分开的,那么他们也是需要去对应的服务器进行访问或者自己再来一个服务
很明显,当这些代码越来越多时,对应服务器的联系也就越来越多,这时,当出现某个服务器死机或者重复模块越来越多
可能会影响整个业务或者需要空间变大,即耦合度越来越高或者重复代码越来越多
所以我们需要将这些重复的代码抽取出来,形成一个一个的服务器
于是就出现了分布式服务架构(完全的分布式了),使得将共用的代码放在一起
实际上就是将所有重复的服务分离出来,变成服务器,而不是模块变成服务器,从而实现真正的分布式的操作
然后我们只需要维护这些服务器即可,而不用防止其他服务器的死机造成访问不了(虽然也会死机,但变成了单独的了,即不影响其他业务),且解决了重复问题
垂直应用架构,也可以说是分布式的操作,也是由多个人(模块或者系统)组成一件事情(项目)
只是在上级的模块进行分布而已,对于整体来说,并没有进行完全分布式(服务并没有分布式,有重复,或者有很大的联系)
分布式服务架构(使得垂直架构,变成真正的分布式的概念,他也是一种服务):
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,如服务抽取出来
作为独立的业务(服务器),逐渐形成稳健的服务中心
使前端应用能更快速的响应多变的市场需求
此时,用户提高业务复用及整合的分布式服务框架(RPC)远程调用是关键
下面由一个业务服务器,变成了三个服务器(这三个是重复的),服务分离出来了

在这里插入图片描述

RPC:独立的应用服务器之间,要依靠RPC(Romote Procedure Call)才能调用
实际情况理解:根据上面的垂直应用架构的实际情况理解开始
在解决了利用问题后,但出现的服务器互相访问的问题或者重复问题,为了解决这种情况
我们将对应模块的服务全部抽取出来,形成一个一个的服务器,那么他们只需要访问这些服务器即可,而他们之间可以实现远程调用,一般都是操作业务层的
使得对应的方法可以远程调用,而不是在一个服务器里,使得每个服务器可能都有对应的方法
但是虽然我们进行了抽取,可是这些公共的服务的访问一般不是平均的,就如下面一样的解释
物流服务不忙,有100台服务器,商品服务特别忙,也是100台服务器,也就是说
虽然是一个服务器,但是里面的服务访问不同,这就又回到了类似单一的架构
只是这些是服务,并不是服务器,所有我们需要进行其他优化(使用不了集群的操作)
那么就有下面的问题:
如何做到资源优化调配:
流动计算架构:
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐呈现
此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率
此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键

在这里插入图片描述

SOA:面向服务架构(Service-Oriented Architecture),简单理解就是"服务治理",例如:公交车站的"调度员"
也可以理解为负载均衡的那个平均访问(只是例子)
但实际上通常整体来说并没有这样的平均操作,因为用户的选择我们基本干涉不了,我们只能对对应服务进行优化
但我们也可以将这些服务完全的分开(虽然他本身也可以,但我们通常将这种称为别名是微服务的了,所以说,微服务可以认为是分布式的一个极致而已,留下少数也可以称为微服务),这样就好操作了,就如垂直应用架构类似的分开(实际上并不是,只是比喻)
这样的操作就是微服务了,以后会进行说明,现在我们只操作该分布式服务架构
Dubbo简介:
Dubbo是分布式服务框架,是阿里巴巴的开源项目,现交给apache进行维护
Dubbo致力于提高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案
简单来说,dubbo是个服务框架,如果没有分布式的需求,是不需要用的
RPC:
RPC(Remote Procedure Call)是指远程过程调用,是一种进程间通信方式
RPC基本的通信原理(可以认为是网络编程上(tcp),进一步补充操作,当然,我们编程语言一般只能操作电脑上存在的层,在传输层后面就不行了,都不是电脑的怎么操作呢(具体可以百度)):
在客户端将对象进行序列化
底层通信框架使用netty(基于tcp协议的socket),将序列化的对象发给服务方提供方
服务提供方通过socket得到数据文件之后,进行反序列化,获得要操作的对象
对象数据操作完毕,将新的对象序列化,再通过服务提供方的socket返回给客户端
客户端获得序列化数据,再反序列化,得到最新的数据对象,至此,完成一次请求

在这里插入图片描述

RPC两个核心模块:通讯(socket),序列化
节点角色:

在这里插入图片描述

在这里插入图片描述

调用关系:
服务容器负责启动,加载,运行服务提供者
服务提供者在启动时,向注册中心注册自己提供的服务
服务消费者在启动时,向注册中心订阅自己所需的服务
在注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用
服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
快速入门 :
官网:http://dubbo.apache.org/

在这里插入图片描述

注册中心:
Zookeeper:
官方推荐使用zookeeper注册中心,因为在上图中,我们发现,这个注册中心的操作就类似于zookeeper,即也有通知
如可以将对应的列表查询给你,而这样的操作,实际上在界面上我们并不会体会到,因为他只是文件系统加上通知而已
至于如何显示,要看你们如何操作
注册中心负责服务地址的注册与查找,相当于目录服务
服务提供者和消费者只在启动时与注册中心交互,注册中不转发请求,压力较小
Zookeeper是apache hadoop的子项目,是一个树形的目录服务,支持变更推送
适合作为dubbo的服务注册中心,工业强度较高,可用于生产环境;
dubbo即是求职的人,也是招聘单位,而zookeeper就是人才市场/招聘网站
安装:
安装jdk(具体看55章博客)
前面78章博客里有操作,若看过,并操作过,可以直接跳过
拷贝apache-zookeeper-3.6.0-bin.tar.gz到opt目录
解压安装包:
[root@localhost opt]# tar -zxvf apache-zookeeper-3.6.0-bin.tar.gz
重命名:
[root@localhost opt]# mv apache-zookeeper-3.6.0-bin zookeeper
在/opt/zookeeper/这个目录上创建zkData和zkLog目录
[root@localhost zookeeper]# mkdir zkData
[root@localhost zookeeper]# mkdir zkLog
进入/opt/zookeeper/conf这个路径,复制一份 zoo_sample.cfg 文件并命名为 zoo.cfg
[root@localhost conf]# cp zoo_sample.cfg zoo.cfg
编辑zoo.cfg文件,修改dataDir路径:
dataDir=/opt/zookeeper/zkData
dataLogDir=/opt/zookeeper/zkLog
启动Zookeeper:
[root@localhost bin]# ./zkServer.sh start
查看状态:
[root@localhost bin]# ./zkServer.sh status
注意:上面只是单机启动,若有集群的配置,注意进行注释(可以选择百度查看,或者看78章博客)
服务提供方:
一个空的maven项目
提供一个服务接口即可
服务方的pom.xml:
各种依赖请严格按照下面的版本(因为不同的版本,可能会不兼容,后面的操作时,也尽量不要修改版本,防止启动失败)
<packaging>war</packaging>
<properties>
    <spring.version>5.0.6.RELEASE</spring.version>
</properties>
<dependencies>
    <dependency>
         <!--有对应的类操作页面,如前端控制器,有下面的基础依赖-->
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <!--基础依赖,其他依赖一般会导入这个,这里进行版本操作一下
实际上可以不写(因为其他依赖基本都有这个)
这个jar 文件包含Spring 框架基本的核心工具类
Spring 以及其它组件基本都要使用到这个包里的类,是其它组件的基本核心,当然你也可以在自己的应用系统中使用这些工具类
-->
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <!--基础依赖,其他依赖一般会导入这个,这里进行版本操作一下,实际上可以不写
这个jar 文件是所有应用都要用到的,它包含访问配置文件,创建和管理bean
以及进行IoC/DI(控制反转和依赖注入)操作相关的所有类
如果应用只需基本的IoC/DI 支持,引入spring-core.jar 及spring-beans.jar 文件就可以了
-->
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <!--IOC容器的对象需要-->
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <!--上面的IOC的扩展,具体百度,可以不写
即包括UI模版,邮件服务,脚本服务,缓存,任务计划等方面的类-->
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <!--tx的相关事务操作-->
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <!--dubbo 有对应的注解,如@Service-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>dubbo</artifactId>
        <version>2.5.7</version>
    </dependency>
    <dependency>
        <!--对应的包需要,如ZooKeeper对象创建,即这个类的使用-->
        <groupId>org.apache.zookeeper</groupId>
        <artifactId>zookeeper</artifactId>
        <version>3.4.6</version>
    </dependency>
    <dependency>
        <!--启动成对应的客户端或者服务端必须要,否则报错-->
        <groupId>com.github.sgroschupf</groupId>
        <artifactId>zkclient</artifactId>
        <version>0.1</version>
    </dependency>
    <dependency>
        <!--操作class文件的,这里并没有用到,可以删除-->
        <groupId>javassist</groupId>
        <artifactId>javassist</artifactId>
        <version>3.11.0.GA</version>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.tomcat.maven </groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <configuration>
                <port>8001</port>
                <path>/</path>
                <uriEncoding>UTF-8</uriEncoding>
                <!--一般的tomcat是可以识别URL中的中文的,但是maven默认基本上是识别不了,所以需要设置
若不设置的话,后面你再怎么设置utf-8,都没有用
因为他已经是乱码了,即已经有一个不对编码了(最开始不对)
-->
            </configuration>
             <executions>
                <execution>
                    <!-- 打包完成后,运行服务 -->
                    <phase>package</phase>
                    <goals>
                        <goal>run</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

服务方接口:
package service;

/**
 *
 */
public interface HelloService {
    String sayHello(String name);
}

服务方实现 :
package service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import service.HelloService;

/**
 *
 */
@Service 
//是dubbo的注解,不是spring的注解,主要用来找到对应提供类,即这个类来提供
//通常与@Reference注解一起操作
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        return "Hello," + name + "!!!";
    }
}
服务方的配置文件spring.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://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://code.alibabatech.com/schema/dubbo
           http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <!--提供命名空间和约束,但具体的操作只是被我们识别而已,需要对应依赖,才可以使用对应命名空间
可以看到对应的地址,就是我们的依赖对应的地址
-->
    <!--1.服务提供方在zookeeper中的"别名"-->
    <dubbo:application name="dubbo-server"/>
    <!--2.注册中心的地址-->
    <dubbo:registry address="zookeeper://192.168.164.128:2181"/>
    <!--3.扫描类(将什么包下的类作为服务提供类)
即找到对应的dubbo的@Service注解,进行提供,实际上也到全局了,若不是dubbo的@Service注解,则报错,访问不了
即不提供(全局没有),而这时,若直接指定(如指定版本),则启动时,就会报错(因为他必须要是进行提供的类才可)

当然,若我们手动的进行读取配置文件,就如spring的读取配置文件一样,那么自然也是可以得到该实例的
而不是默认的全局实例(基本只能操作controller层)
-->
    <dubbo:annotation package="service.impl"/>
</beans>
服务方的web.xml :
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
        http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         id="WebApp_ID" version="3.1">
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
<!--用来给controller的进行注入的,他只会操作这个注入,即这个容器-->
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/spring.xml</param-value>
    </context-param>
    <!--将对应的实例放在全局的ServletContext里面的就是服务方,即启动时
会创建对应实例对应接口的节点,并报出该服务方信息,如地址
该实例需要实现方法,因为启动时,会观察对应实例是否有实现的接口,若都没有,则不是服务方
因为这个接口是需要进行注入的,为什么需要实现接口,因为实现接口,一般解决了部分的操作,也可以使得服务方,只需要有对应接口即可,而不用创建类来对应,主要是因为,类可以实现多个接口,在以后会比类继承一个方便许多
所以一般规定需要实现接口
最后因为消费方在与服务方通信时,服务方发送的数据就是从全局里面找到对应实现的接口的类的实例进行序列化数据发送
所以,在有全局ServletContext和有实例的情况下,就是服务方,否则不是服务方-->
</web-app>
打包运行,测试一下是否成功(一般的对应的依赖,会使得出现不同日志,这个不必理会)
服务消费方:
消费方的pom.xml :
与服务方一致,只需要修改tomcat的端口为8002
消费方的Controller
package controller;

import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import service.HelloService;

/**
 *
 */
@Controller
public class HelloAction {

    @Reference //去服务方将service的实现类注入进来
    private HelloService helloService;

    //@GetMapping("hello")
    @GetMapping(value = "hello",produces = "application/json; charset=utf-8") 
    //多个参数,对应就需要显示的写出value了,单个可以不写,则默认value
    //produces = "application/json; charset=utf-8"这个设置
    //是防止你使用@ResponseBody注解的对应的响应时,出现中文乱码
    @ResponseBody
    public String sayHi(String name) {
        String s = helloService.sayHello(name);

        return s;
    }
}

消费方的接口 :
注意:
controller中要依赖HelloService,所以我们创建一个接口
这里是消费方,不需要实现,因为实现会让服务方为我们搞定(上面的@Reference注解操作注入了)
package service;

/**
 *
 */
public interface HelloService {

    String sayHello(String name);
}

消费方的spring.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://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://code.alibabatech.com/schema/dubbo
           http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <!--1.服务提供方(只是确定位置,虽然这里是消费者别名,我们统称为服务提供,因为都是要连接zookeeper的)在zookeeper中的"别名"-->
    <dubbo:application name="dubbo-consumer"/>
    <!--2.注册中心的地址-->
    <dubbo:registry address="zookeeper://192.168.164.128:2181"/>
    <!--3.扫描类(将什么包下的类作为消费类)-->
    <dubbo:annotation package="controller"/>
    
    <!--
通过这个配置文件,说明一下,为什么可以使用@Reference注解访问远程的实现类
这三个配置,实际上是一个在dubbo设置的参数
第三个注释里说,作为提供类,那么这些类就相当于给其他访问者进行提供的,注意:这时对应的类需要实现接口
否则报错,这是dubbo的规定,下面会说明为什么要这样规定
我们在进行运行时,他会将类实现的接口的包地址作为dubbo下面的节点
dubbo的程序操作,他是我们导入的依赖,也就是有对应类的初始操作,因为有对应参数
该地址包含许多信息
启动服务方时可以在zookeeper里看到对应的节点,出现了dubbo节点(持久型的节点,当再次进行运行时,会进行覆盖)
实际上就是先删除在创建
该节点下,直接就是地址名称的节点了,该地址名称节点,后面有许多信息,也就是服务器的信息,使得可以远程通信
一般就是前面所说的RPC远程服务调用方案中的进行远程通信的数据
通过该信息(如ip地址和端口),操作tcp协议或者udp协议,而不是http或者https的访问
使得得到序列化的数据(该序列化数据是经过对应的服务方处理过的,基本只有对应的返回值操作)
可以打印看看结果,一般是代理类,比如我打印的(com.alibaba.dubbo.common.bytecode.proxy0@25743c47)
每次的重新调用该值不同
注意:需要重新调用方部署,使得变量重新获得才可,被调用方部署没用,即调用方只确定一个类,否则是一样的
而若有多个类进行实现同一个接口,则按照谁类的字母大进行覆盖,如zz类覆盖aa类,使用zz类的,即一般通过ascii的值
而他除了会提供外,也会进行扫描
若有@Reference注解,那么在操作这个变量时,监听他操作,在他操作之前进行赋值
就会去参数设置的注册中心地址,也就是zookeeper中,根据需要注入的类型,一般都是接口
所以我们的消费方,对应的接口除了应对编译期外
也是需要对应同样地址的接口(否则找不到节点,一般就会出现空指针异常,字节变成字符串的操作就会出现)
根据我们写的地址,他会找到对应的节点
这样dubbo会得到该节点信息,通过网络通信,来传输序列化的数据,经过反序列化,并创建一个类来给我们使用
然后给dubbo使用该类赋值给注解对应的信息,且是对应接口的实现类,即dubbo操作进行注入
但是注意:dubbo节点会记录服务方是否关闭,也就是说,若服务方关闭,则通信不了
虽然会去节点找地址,但只会找一次(因为缓存保存),但发现还是通信不了,即会报错(即只要关闭服务方,那么基本就会通信不了),每次刷新都去调用地址的类
若服务方关闭,且dubbo节点删除,也会报错
基本是其他错误(在确定通信不了时,会重新去dubbo里找对应服务方地址,删除了该节点,自然会有错误)
若只删除节点(在消费方访问了的情况下)
实际上是我们进行了访问,这里就这样说了,当然也可不操作,访问了即可
则不会报错(高可用,后面会说明),因为我们已经知道了服务方的地址(本地缓存)
即可以通信了,但是可能会影响对应需要节点的值
因为虽然只需要地址,但若没有节点,对应的操作可能就是默认的
如权重,那么对应的加倍和减半的操作,就不起作用了(在节点里),即使用默认,所以最好不要删除
实际上只是没有对应节点信息,所以使用默认,而由于他是全局的
所以,若对该服务方进行倍权和半权,那么对应的服务(即名称),无论是重启还是刷新,都是这个(因为全局)
若在消费方没有操作对应注解变量的情况下(即没有访问),删除节点
那么由于第一次得不到节点(因为需要服务方的地址,使得通信),会报空指针异常
因为只有操作了这个注解变量,dubbo才会去读取节点地址数据
若先启动服务方,然后删除服务方,而不删除节点,那么消费方可以启动,但这时若消费方访问
会找不到服务方,而使得操作时,直接出现空指针异常(第一次若没有得到对应实例,则默认给出null),而不是报错
所以在没有操作之前,一般只会有空指针异常(自己写的代码)
所以当没有操作对应注解变量时,删除节点和删除服务方,就相当于默认的一个普通项目了
那么一般也是空指针异常,先去节点,出现报错,返回null,但这时,无论怎么刷新都是null了
因为已经知道得不到地址,所以防止多次刷新,就默认为null了
上面都是注解@Reference的操作造成的
但是若重新启动服务方就不会,因为不看时间(只对删除服务方来说)
因为有对应的地址,所以多次刷新,都是从对应地址里进行调用
即上面的找地址,基本只会与名称和别名有关,与时间也基本无关
所以虽然zookeeper只是文件系统加通知,但dubbo的操作,在执行时,是会有找到别人提供的类中,进行注入
而这个别名就是具体的服务方,别名不做要求,即可以随便写(基本只是确定是谁来进行了操作而已),若出现两台及其以上的同样的接口提供,对应的节点只有一个
但运行时,那么则按照对应的权重来进行,默认随机,后面的负载均衡介绍里面会说明
上面就是dubbo如何远程调用的具体原因
-->
</beans>
但要注意:记得将对应别名改成dubbo-consumer(你也可以自己写别名,可以与这个不一样)
消费方的web.xml :
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
        http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         id="WebApp_ID" version="3.1">
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/spring.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
       <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
启动服务测试:
首先启动服务方(创建了对应节点),再启动消费方
访问:http://localhost:8002/hello?name=james
发现返回了结果,至此,我们进行了远程的调用的方式
监控中心 :
我们在开发时,需要知道注册中心都注册了哪些服务,以便我们开发和测试
图形化显示注册中心的中 服务列表
我们可以通过部署一个web应用版的管理中心来实现
主要看服务方数据的显示和消费方数据的显示
服务管理端:
安装管理端:
下载地址:
链接:https://pan.baidu.com/s/17BBhrPQsFr0o0_2MJBeujw
提取码:alsk
解压 dubbo-admin-master.zip,实际上也是一个项目,或者说web项目,只是他用来显示对应图形化界面而已
实际上主要操作对应dubbo在zookeeper的dubbo节点里的节点的显示或操作,使得图形化了,就如Mysql的图形化一样
修改配置文件

在这里插入图片描述

在这里插入图片描述

这个注册中心地址修改成自己的zookeeper所在的服务器地址
返回到项目根目录,使用maven打包:mvn clean package

在这里插入图片描述

在cmd窗口中,linux的ls命令不是查找目录文件的,而是dir来查找,我们可以到target目录下
即在dos下运行target目录中的jar文件:java -jar dubbo-admin-0.0.1-SNAPSHOT.jar,相当于服务器运行war包一样
只是war包是包括页面的,而jar基本只包括class(当然也可以包括页面,如jsp的输出流操作)
war包可以运行在服务器,而jar包不可以(只能使用)

在这里插入图片描述

注意:运行后,对应窗口不要删除,否则会停止服务器的操作,就如tomcat的窗口删除,也就关闭了服务器
此时打开浏览器输入:http://localhost:7001/
第一次访问时,需要登录,帐号密码都是root
看到如下界面,说明这个项目(可以说是web项目)执行成功
会操作获取的dubbo节点,并记录时间,以后会根据时间进行更新,没找到不更新,即也可以说是保存

在这里插入图片描述

管理端使用:
启动服务方,将服务注册到zookeeper
在这之前,我们首先点击一下上面的箭头:

在这里插入图片描述

启动dubbo-server服务方后,刷新管理端,服务注册成功,只是没有消费者

在这里插入图片描述

点击服务名,进入服务提供者页面

在这里插入图片描述

把消费者也运行起来,注意要运行,这个运行实际上是使用对应的@Reference注解操作的变量
即需要被操作,dubbo看到被使用后,监听的操作
会在对应节点加上数据,而这个数据使得下面显示正常(所以不要乱删除节点,使得数据被固定)
刷新服务,显示正常

在这里插入图片描述

注意:若出现特殊的问题,可以重新运行项目,如明明消费者已经启动且运行,却显示没有消费者
而且还有很多问题,而出现这些问题的原因,主要是因为我们手动去删除zookeeper的dubbo节点
可能该节点与该web项目有关系吧,而正是删除了,当该项目去访问时(访问对应时间的节点)
所以删除节点,在没有刷新时,也是会没有找到对应数据的,而没有找到对应节点数据时
就会使得原来的显示固定保存起来
而由于优先显示同样的固定的数据(实际上只会得到对应时间的节点,所以相当于保存起来了)
所以你会发现,明明我们消费者运行,却没有变化,因为使用的是固定的数据
所以,最好不要进行删除
查看消费者(远程调用的基本就是消费者,反之就是服务提供者):

在这里插入图片描述

监控统计中心:
Monitor:统计中心 ,记录服务被调用多少次等,主要看服务方数据的显示
下载地址:
链接:https://pan.baidu.com/s/1MmR6SBkvoZVsECLSqePRAg
提取码:alsk
解压dubbo-monitor-simple-2.5.3.zip
修改dubbo-monitor-simple-2.5.3\conf\dubbo.properties

在这里插入图片描述

其实只需要将对应的zookeeper的配置的注释去掉即可,原来的默认的可以注释掉,这里就直接修改默认的了
其中对应的端口是8080,会占用,这个要注意以下,防止端口冲突,使得启动不了(一般会让你按下任意键退出)
双击运行dubbo-monitor-simple-2.5.3\bin\start.bat
注意:对应的端口不要关闭,否则就如tomcat一样
服务器也就关闭了(可以这样说,但只是针对于服务),实际上就是服务关闭了
分别修改dubbo-server项目和dubbo-consumer项目的spring.xml,加入下面标签
<!-- 让监控 去注册中心 自动找服务 ,实际上就是统计这个的次数,如调用和被调用次数,没写的不统计-->
<dubbo:monitor protocol="registry"/>
最好服务方和消费方都写上,防止对应的次数不正确(即不计数)
他的刷新不是实时的,有一定间隔刷新一次(即无论你怎么刷新网站都没有用,必须等待一定间隔才会更新数据)
但其中一个不写的话,可能会出现次数不一致(好像是不计数)

在这里插入图片描述

其中Statistics那里就是下面的操作

在这里插入图片描述

代表26个请求,成功26个请求,实际上左边的一般与消费方有关(调用次数,必须要调用完毕,中途报错不算)
右边的与服务方有关(被调用次数,基本只要执行就算,即被调用就算)
都与上面的监听有关(即没有配置,则不增加对应的次数)
而这些数据,就存放在对应的zookeeper里面,发送或者接受都会改变对应的数据,一般都会相同,除非有不监听的
综合实战:
配置说明:
启动时检查
在Dubbo的官网:http://dubbo.apache.org/里找到(快速开始),出现如下(可能现在与下面不同了):

在这里插入图片描述

启动时会在注册中心检查依赖的服务是否可用(或者是否有服务在zookeeper里面),不可用时会抛出异常
在消费方编写初始化容器的main方法启动(tomcat启动方式,必须访问一次action才能初始化spring)
package text;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.io.IOException;

/**
 *
 */
public class Test {
    public static void main(String[] args) throws IOException {
        ClassPathXmlApplicationContext context = new 
            ClassPathXmlApplicationContext("classpath:spring/spring.xml");
        //进行读取文件,使得操作dubbo的参数
        System.in.read();
    }

}

为了更好的观察,则配置系统级别日志,需要配合log4j才输出,在resources下添加log4j.properties,内容如下:
### log4j.appender.stdout = 表示输出方式
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
#日志输出到系统标准输出位置上,默认就是这个值
log4j.appender.stdout.Target=System.out
# 表示输出格式
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 打印信息格式
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %m%n
### log4j.appender.file = 表示文件输出方式
log4j.appender.file=org.apache.log4j.FileAppender
#文件存放位置
log4j.appender.file.File=dubbo.log
# log4j.appender.file.layout = 表示输出格式
log4j.appender.file.layout=org.apache.log4j.PatternLayout
# log4j.appender.file.layout.ConversionPattern = 表示打印格式
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %m%n
# log4j.rootLogger = 表示根日志级别
log4j.rootLogger=error, stdout,file
写上后,我们再次进行运行,那么就出现了新的日志,即报没有对应的服务错误
实际上是自身读取配置文件时,提供不了服务或者对应没有服务,即只有是消费者(可以这样说)
但并不会影响运行,只是没有提供服务而已
而当有提供服务时(自己提供服务,在检查之前),就不会出现错误,但虽然不会影响运行,但是错误还是要进行消除的
实际上最好不要消除错误,而是要解决错误,因为可以根据错误知道那里有问题
虽然这个问题在启动时,不会中断,即可以启动,但是在运行时,若没有解决他的报错原因,通常来说,都会报错
我们在消费方的spring.xml加上如下配置:
<!--默认是true:抛异常;false:不抛异常,对与是否是符合服务有关,即是否检查,即是否有提供服务-->
<dubbo:consumer check="false" />
使得不会去检查服务是否有提供
超时时间:
由于网络或服务端不可靠,会导致调用过程中出现不确定的阻塞状态(超时)
为了避免超时导致客户端资源(线程)挂起耗尽,必须设置超时时间
我们在服务方的spring.xml加上如下配置
<!--设置超时时间为2秒,默认为1秒-->
<dubbo:provider timeout="2000"/>
可以将服务实现HelloServiceImpl.java中加入模拟的网络延迟进行测试:
如下:
package service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import service.HelloService;

/**
 *
 */
@Service
public class HelloServiceImpl implements HelloService{

    public String sayHello(String name) {

        try {
            Thread.sleep(3000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "hello " + name;
    }
}
超时设置2秒,而模拟的网络延迟有3秒,超出时限,报错,可以看到页面出现错误
配置原则:
dubbo推荐在Provider(服务方)上尽量多配置Consumer(消费方)端属性(因为服务方是被调用的):
作为服务的提供者,比服务使用方更清楚服务性能参数,如调用的超时时间,合理的重试次数,等等
在Provider(服务方)配置后,Consumer(消费方)不配置或者配置都会使用Provider(服务方)的配置值(对于不同的配置)
因为调用,在调用进行远程通信时
对应方法返回值给消费方,即处理过的数据,进行序列化给消费方的,然后消费方进行反序列化,一般只有返回值
而对应方法的执行给服务方,即处理中会进行调用,返回值会形成一个新的对象
且操作实现了对应的接口,可能是直接的写入,进行序列化,给消费方
所以一般使用服务方的配置,他们的使用依据,实际上的调用时序列化时进行的,每次调用都会将对应的配置参数进行赋值
即配置的操作基本是消费方在操作
使得使用服务方的配置(相同的配置下,不同配置使用自己的,即这里对应消费方)
当然,若服务方没有设置,而消费方设置了,则按照消费方的(这时消费方优先于默认)
所以就有如下解释:
在相同配置下(默认使用服务方的):
若服务方配置,消费方没有配置,则使用服务方的(服务方优先于默认)
若服务方没有配置,消费方配置,则使用消费方的(消费方优先于默认)
若服务方配置,消费方也配置,则使用服务方的(相同配置下,优先服务方),即相同配置,服务方会覆盖
在不同配置下(默认使用自己方的,通常都是消费方):
若服务方配置,消费方没有配置,则使用服务方的(服务方优先于默认)
对应的消费方的参数没写的话,则服务方相当于全局,如dubbo:reference标签没有写重试次数,则使用服务方的
若服务方没有配置,消费方配置,则使用消费方的(消费方优先于默认)
若服务方配置,消费方也配置,则使用消费方的(不同配置下,使用自己方,即对于消费方和服务方而言,优先服务方)
若对应的服务方不操作配置,如dubbo:reference在服务方里没有操作配置
由于会覆盖,那么操作服务方,而又由于服务方没有调用,即操作,那么则是默认值
即使用对应的服务方配置,而进行设置dubbo参数,所以也可以说,服务方是优先的
且无论是否自己有配置,都是服务方优先,或者被调用方优先
那么对应的操作也就是设置调用的,即Provider(服务方)配置可以作消费者的缺省值
你可以试着,将对应超时的设置,随便的在消费方里设置,会发现,还是操作服务方的超时设置
重试次数:
当出现失败,自动切换并重试其它服务器,dubbo重试的缺省值默认是2次(除了本次),我们可以自行设置
在provider提供方(也可以说是服务方)配置(添加):
<!-- 消费方连接第1次不算,再来重试3次,总共重试4次,重试次数不包括自己去的那一次-->
<dubbo:provider timeout="2000" retries="3"/> 
<!--若超时了没有连接,则继续重试,直到重试了3次还没有连接,则报错
与超时的错误一样的,因为这里只是进行重试,最后没有重试了,自然就是超时的错误-->
修改对应实现类来测试:
package service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import service.HelloService;

/**
 *
 */
@Service
public class HelloServiceImpl implements HelloService{

    public String sayHello(String name) {
        System.out.println("被调用一次");
        try {
            Thread.sleep(3000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "hello " + name;
    }
}
发现有四个输出"被调用一次"然后出现错误
并不是所有的方法都适合设置重试次数
幂等方法:适合(当参数一样,无论执行多少次,结果是一样的,例如:查询,修改)
非幂等方法:不适合(当参数一样,执行结果不一样,例如:删除,添加)
这时防止出现数据问题,如出错或者多余数据(删除添加)
当然还有某些业务方面的数据也是不适合的,如对应字段值的增加和减少等等,也是基本不适合的
单独设置某个方法
提供方接口添加sayNo()方法并实现:
package service;

/**
 *
 */
public interface HelloService {
    String sayHello(String name);
    String sayNo();
}

package service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import service.HelloService;

/**
 *
 */
@Service
public class HelloServiceImpl implements HelloService{

    public String sayHello(String name) {
        System.out.println("被调用一次");
        try {
            Thread.sleep(3000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "hello " + name;
    }

    @Override
    public String sayNo() {
        System.out.println("-------no被调用一次-------");
//        try {
//            Thread.sleep(3000);
//        } catch (Exception e) {    这里可以加上延迟再次测试
//            e.printStackTrace();
//        }
        return "no!";

    }
}
消费方接口添加sayNo()方法声明:
package service;

/**
 *
 */
public interface HelloService {

    String sayHello(String name);
    String sayNo();
}

消费方controller:
package controller;

import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import service.HelloService;

/**
 *
 */
@Controller
public class HelloAction {

    //@Reference 此注解已经在xml文件中被<dubbo:reference>顶替,所以自动注入即可,后面的配置
    @Autowired
    private HelloService helloService;

    @GetMapping("hello")
    @ResponseBody
    public String sayHi(String name) {
        String s = helloService.sayHello(name);

        return s;
    }

    @GetMapping("no")
    @ResponseBody
    public String no() {
        String s = helloService.sayNo();
        return s;
    }
}

消费方配置方法重试次数(在消费方的spring.xml添加如下):
<dubbo:reference interface="service.HelloService" id="HelloService">
        <dubbo:method name="sayHello" retries="3"/>  <!--retries设置对应的调用方法的重试次数-->
    <!--若是服务方的话,则会当作全局,被覆盖的话,都是默认,因为服务方不操作-->
        <dubbo:method name="sayNo" retries="0"/> <!-- 不重试 -->
    
    </dubbo:reference>
<!--这个标签会操作@Reference注册的操作,即当调用对应方法时,就会去进行通信注入
并放在容器里,最后spring操作时,可以使用注入方式(即在spring里,即读取配置文件时,进行了操作)

命令空间实际上也可以说是对应依赖的包给出的,当我们读取这些配置时,会于对应依赖包联系(因为他给出,有地址)
然后他进行操作,操作对应前缀的名称的配置


这里我们不在对应的服务方里进行设置重试次数
因为会覆盖(对于消费方没有写对应的参数而言,则使用服务方的或者服务方的默认)


最后注意:若@Reference注解与dubbo:reference一起使用,则默认使用@Reference注解,即这个标签就不起作用了
-->
多版本:
一个接口,多个(版本的)实现类,可以使用定义版本的方式引入
为HelloService接口定义两个实现类,提供者添加如下配置:
<dubbo:service interface="service.HelloService"
                   class="service.impl.HelloServiceImpl1" version="1.0.0"/>
    <dubbo:service interface="service.HelloService"
                   class="service.impl.HelloServiceImpl2" version="2.0.0"/>
消费者就可以根据version的版本,选择具体的服务版本:
 <dubbo:reference interface="service.HelloService" id="HelloService" version="1.0.0">
     <!--虽然覆盖,但是操作的是对应的1.0.0的实现类,原来只有一个实现类,当多个实现类时,需要这样设置了-->
        <dubbo:method name="sayHello" retries="3"/>
        <dubbo:method name="sayNo" retries="0"/> <!-- 不重试 -->
    </dubbo:reference>
<!--若不指定或者没有对应的指定
则默认操作类名的ASCII的值来判断,谁大,就调用谁,对应的@Reference注解也是一样的-->
注意:消费者的控制层要改为自动注入,因为@Reference注解和 dubbo:reference在这里冲突
因为一起的话,这个dubbo:reference标签不起作用
修改对应的controller层的类:
 @Autowired
    private HelloService helloService;
当消费者的版本修改为 version=“*”,那么就会随机调用服务提供者的版本(随机操作设置的版本),若没有对应的设置版本
即操作的这个dubbo:service标签,就不会执行方法,即报错(不是对应的超时错误)
/*
-------1.0被调用一次-------
-------2.0被调用一次-------
-------1.0被调用一次-------
-------1.0被调用一次-------
-------1.0被调用一次-------
-------2.0被调用一次-------
*/
//上面的测试的结果
本地存根:
目前我们的分布式架构搭建起来有一个严重的问题,就是所有的操作全都是消费者发起,由服务提供者执行
消费者动动嘴皮子却什么活都不干,这样会让提供者很累
例如简单的参数验证,消费者完全能够胜任,把合法的参数再发送给提供者执行,效率高了,提供者也没那么累了
例如:去房产局办理房屋过户,请带好自己的证件和资料,如果什么都不带,那么办理过户手续会很麻烦
得先调查你有什么贷款,有没有抵押,不动产证是不是你本人,复印资料等操作,一天肯定办不完,明天还要来
如果你能提前将这些东西准备好,办理过户,1个小时足矣,这就是"房产中介办事效率高的原因"
话不多说,先在消费者处理一些业务逻辑,再调用提供者的过程,就是"本地存根"
代码实现肯定在 消费者,创建一个HelloServiceStub类并且实现HelloService接口
注意:必须使用构造方法的方式注入
package stub;

import org.springframework.util.StringUtils;
import service.HelloService;

/**
 *
 */
public class HelloServiceStub implements HelloService {

    private HelloService helloService; //相当于一个代理对象,实际上也是

    //本地存根,必须以构造方法的形式注入
    public HelloServiceStub(HelloService helloService) {
        this.helloService = helloService;
    }

    @Override
    public String sayHello(String name) {
        if(!StringUtils.isEmpty(name)) {
            return helloService.sayHello(name);
        }
        return "哈哈哈";
    }

    @Override
    public String sayNo() {
        return null;
    }
}

修改消费者配置:
<dubbo:reference interface="service.HelloService" id="HelloService" version="1.0.0" 
stub="stub.HelloServiceStub">
    <!--这里将得到的版本,即会将得到的实例,通过构造方法来进行赋值给这个类,该类给IOC容器,然后在进行注入
当然,若使用@Reference注解,自然的,这个配置不起作用了,即默认操作了

-->
        <dubbo:method name="sayHello" retries="3"/>
        <dubbo:method name="sayNo" retries="0"/> <!-- 不重试 -->
    </dubbo:reference>

<!--发现,的确可以在消费方那里,再次进行操作-->
负载均衡策略:
负载均衡(Load Balance), 其实就是将请求分摊到多个操作单元上进行执行,从而共同完成工作任务
简单的说,好多台服务器,不能总是让一台服务器干活,应该"雨露均沾"
dubbo一共提供4种策略,缺省(默认的)为 random 随机分配调用
权重默认是一样的,都是初始值,默认为100,在dubbo的web项目里可以看到
一般的权重都会有检查,也就是说,对应的权重必须是大于0的整数,否则会报错(nginx的也是如此)

在这里插入图片描述

随机的:基本是根据几率了
轮询的:这里是根据侧重的显示,即并不是完全的一定是一次一次的操作,但是顺序还是有的,如到了2这里,可能会多出现几次
但总体来说,是按照顺序,局部来说,可以说是按照几率
活跃的:根据速度来决定是否访问,相同活跃就是随机(真正的随机,不是几率),不同,则进行侧重,可以理解为变化的权重
一致的:根据自己来决定
修改提供者配置并启动3个提供者,让消费者对其进行访问
tomcat端口8001,8002,8003
provider端口20881,20882,20883
<dubbo:provider timeout="2000" port="20883"/>  <!--对应的方法的端口,不能相同,否则启动不了,占用-->
<!--20883这里,每次启动改一下-->
对应的2.0.0版本的类,可以给打印语句,加上标识,如服务器1,服务器2,服务器3等等
打开对应的dubbo的web项目,发现有4个,总共两个
第一个,其中三个代表对应两个方法和自己的版本(即下面的第二个),即总体
第二个,一个,则是对应版本的一个,上面都是一个端口的,不同端口又是一组
这样的说明,只有等你进行测试时,可以更加明白
启动consumer进行测试
改变策略:
 <dubbo:reference loadbalance="roundrobin" interface="service.HelloService" id="HelloService" 
                  version="2.0.0" stub="stub.HelloServiceStub">
     <!--loadbalance="roundrobin"修改为轮询策略,基本是使用对应单词的第一个单词(全部小写,作为参数)-->
        <dubbo:method name="sayHello" retries="3"/>
        <dubbo:method name="sayNo" retries="0"/> <!-- 不重试 -->
    </dubbo:reference>
再次启动consumer进行测试
消费方修改权重:
最好使用管理端修改权重:

在这里插入图片描述

这个倍权(乘2,加一倍)和半权(除2,减一半),用来改变权重,这个权重是全局的
绑定了对应服务方的名称,即无论是重启还是刷新,都是这个改变后的权重,除非删除节点
高可用:
zookeeper宕机:
zookeeper注册中心宕机,还可以消费dubbo暴露的服务
监控中心宕掉不影响使用,只是丢失部分采样数据
数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
注册中心对等集群,任意一台宕掉后,将自动切换到另一台
注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯(下面的测试)
服务提供者无状态,任意一台宕掉后,不影响使用
服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
测试:
先正常发出请求
关闭zookeeper:./zkServer.sh stop
消费者仍然可以正常消费
服务降级:
壁虎遇到危险会自动脱落尾巴,目的是损失不重要的东西,保住重要的
服务降级,就是根据实际的情况和流量,对一些服务有策略的停止或换种简单的方式处理
从而释放服务器的资源来保证核心业务的正常运行
为什么要服务降级:
而为什么要使用服务降级,这是防止分布式服务发生雪崩效应
什么是雪崩:
就是蝴蝶效应,当一个请求发生超时,一直等待着服务响应,那么在高并发情况下
很多请求都是因为这样一直等着响应,直到服务资源耗尽产生宕机,
而宕机之后会导致分布式其他服务调用该宕机的服务也会出现资源耗尽宕机
这样下去将导致整个分布式服务都瘫痪,这就是雪崩
简单来说就是:分布式系统中经常会出现某个基础服务不可用造成整个系统不可用的情况,这种现象被称为服务雪崩效应
服务降级实现方式:
在 管理控制台配置服务降级:屏蔽和容错
屏蔽:mock=force:return+null 表示消费方对该服务的方法调用都 直接返回 null 值,即消费者不发起远程调用
用来屏蔽不重要服务不可用时对调用方的影响(对于当前的版本服务方而言)
容错:mock=fail:return+null 表示消费方对该服务的方法调用在 失败后,再返回 null 值,不抛异常
用来容忍不重要服务不稳定时对调用方的影响(对于当前的版本服务方而言)
这里是2.0.0版本(自己设置的,你可以修改)

在这里插入图片描述

整合MyBatis实现用户注册:
初始化数据库:
CREATE DATABASE smd CHARACTER SET utf8;
USE smd;
CREATE TABLE users(
 uid INT(11) AUTO_INCREMENT PRIMARY KEY,
 username VARCHAR(50) NOT NULL,
 PASS VARCHAR(50) NOT NULL,
 phone VARCHAR(50) NOT NULL,
 createtime VARCHAR(50) NOT NULL
)
创建聚合项目-项目模块化:
lagou-dubbo(项目目录),创建一个空的项目
lagou-dubbo-parent(父工程,聚合项目(主要作用):定义所有模块用的依赖版本)
<!--当创建子工程时,会默认添加上,也可以手动添加-->
    <!--对于版本控制需要这个pom-->
   <!--且当你是父工程时,这个必须加上,否则会有报错提示(实际上是因为modules标签的存在,使得出现错误提示)-->
<packaging>pom</packaging>

	<properties>
         <spring.version>5.0.6.RELEASE</spring.version>
    </properties>
    <dependencies>
     <!-- JSP相关 -->
     <dependency>
         <!--jstl表达式,没有他,那么如
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
这个就会报红,即到这个页面时,基本就会报错
-->
       <groupId>jstl</groupId>
       <artifactId>jstl</artifactId>
       <version>1.2</version>
     </dependency>
     <dependency>
         <!--对应的Servlet,即需要HttpServletResponse或者HttpServletRequest时需要用到
虽然tomcat自带,但是并不会使用,即需要我们导入
实际上javax.servlet-api和servlet-api差不多,但前者是高版本的,且可能有些地方有扩展(更多功能)
但主要只会使用HttpServletResponse或者HttpServletRequest
-->
       <groupId>javax.servlet</groupId>
       <artifactId>servlet-api</artifactId>
       <scope>provided</scope>
       <version>2.5</version>
     </dependency>
     <dependency>
     <!--jsp的操作,实际上只是用来提示的,也就是说,没有这个,那么使用
${pageContext.request.contextPath}时,打出pageContext后面的.没有提示,但运行时,是使用服务器的操作的
即删除他运行也是没有问题,只是我们操作时会没有提示
-->
       <groupId>javax.servlet</groupId>
       <artifactId>jsp-api</artifactId>
       <scope>provided</scope>
       <version>2.0</version>
     </dependency>
     <!-- Spring -->
     <dependency>
           <!--Spring容器,即IOC容器的使用需要这个,比如ApplicationContext-->
       <groupId>org.springframework</groupId>
       <artifactId>spring-context</artifactId>
       <version>${spring.version}</version>
     </dependency>
     <dependency>
             <!--基础依赖,其他依赖一般会导入这个,这里进行版本操作一下,实际上可以不写
这个jar 文件是所有应用都要用到的,它包含访问配置文件,创建和管理bean
以及进行IoC/DI(控制反转和依赖注入)操作相关的所有类
如果应用只需基本的IoC/DI 支持,引入spring-core.jar 及spring-beans.jar 文件就可以了
-->
       <groupId>org.springframework</groupId>
         <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
          </dependency>
     <dependency>
         <!--有对应的类操作页面,如前端控制器-->
       <groupId>org.springframework</groupId>
       <artifactId>spring-webmvc</artifactId>
       <version>${spring.version}</version>
     </dependency>
     <dependency>
               <!--spring使用连接池的,包括一些tx操作
    若需要tx的一些操作时(如事务传播),那么可以导入tx-->
       <groupId>org.springframework</groupId>
       <artifactId>spring-jdbc</artifactId>
       <version>${spring.version}</version>
     </dependency>
     <dependency>
         <!--提供对AspectJ的支持,也可以说是操作织入-->
       <groupId>org.springframework</groupId>
       <artifactId>spring-aspects</artifactId>
       <version>${spring.version}</version>
     </dependency>
     <!-- Mybatis -->
     <dependency>
         <!--引入mybatis依赖,如工厂的一些类-->
       <groupId>org.mybatis</groupId>
       <artifactId>mybatis</artifactId>
       <version>3.2.8</version>
     </dependency>
     <dependency>
         <!--整合需要,即有对应类,如org.mybatis.spring.SqlSessionFactoryBean-->
       <groupId>org.mybatis</groupId>
       <artifactId>mybatis-spring</artifactId>
       <version>1.2.2</version>
     </dependency>
     <!-- 连接池 -->
     <dependency>
       <groupId>com.alibaba</groupId>
       <artifactId>druid</artifactId>
       <version>1.0.9</version>
     </dependency>
     <!-- 数据库 -->
     <dependency>
          <!--mysql驱动-->
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
       <version>5.1.32</version>
     </dependency>
     <!--dubbo -->
     <dependency>
         <!--dubbo 有对应的注解,如@Service-->
       <groupId>com.alibaba</groupId>
       <artifactId>dubbo</artifactId>
       <version>2.5.7</version>
     </dependency>
     <dependency>
          <!--对应的包需要,如ZooKeeper对象创建,即这个类的使用-->
       <groupId>org.apache.zookeeper</groupId>
       <artifactId>zookeeper</artifactId>
       <version>3.4.6</version>
     </dependency>
     <dependency>
         <!--启动成对应的客户端或者服务端必须要,否则报错-->
       <groupId>com.github.sgroschupf</groupId>
       <artifactId>zkclient</artifactId>
       <version>0.1</version>
     </dependency>
     <dependency>
         <!--操作class文件的,这里并没有用到,可以删除-->
         <groupId>javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.11.0.GA</version>
          </dependency>
         <!-- fastjson -->
         <dependency>
              <!--fastjson工具包 -->
           <groupId>com.alibaba</groupId>
           <artifactId>fastjson</artifactId>
           <version>1.2.47</version>
         </dependency>
         <!-- junit -->
         <dependency>
             <!--@Test的操作-->
           <groupId>junit</groupId>
           <artifactId>junit</artifactId>
           <version>4.12</version>
           <scope>test</scope>
         </dependency>
         <dependency>
            <!--Spring整合@Test,使得可以使用注解指定配置类或者配置文件-->
           <groupId>org.springframework</groupId>
           <artifactId>spring-test</artifactId>
           <version>${spring.version}</version>
           <scope>test</scope> <!--基本只操作test目录,即测试范围使用,且无法传递-->
         </dependency>
    </dependencies>

lagou-dubbo-entity(实体工程,jar项目):

在这里插入图片描述

对应的实体类(Users类):
package entity;

import java.io.Serializable;

/**
 *  uid INT(11) AUTO_INCREMENT PRIMARY KEY,
 *  username VARCHAR(50) NOT NULL,
 *  PASS VARCHAR(50) NOT NULL, 
 *  高版本的mysql不能使用password字段名
 *  大概是5.7.3及其后面的版本,可能不是很具体,现在可能需要更高,参考即可
 *  即操作时,基本报错,高版本移除
 *  一般使用authentication_string代替他(可能有其他方式,百度即可)
 *  phone VARCHAR(50) NOT NULL,
 *  createtime VARCHAR(50) NOT NULL
 */
public class Users implements Serializable {

    private int uid;
    private String username;
    private String pass;
    private String phone;
    private String createtime;

    @Override
    public String toString() {
        return "Users{" +
                "uid=" + uid +
                ", username='" + username + '\'' +
                ", pass='" + pass + '\'' +
                ", phone='" + phone + '\'' +
                ", createtime='" + createtime + '\'' +
                '}';
    }

    public Users() {
    }

    public Users(String username, String pass, String phone, String createtime) {
        this.username = username;
        this.pass = pass;
        this.phone = phone;
        this.createtime = createtime;
    }

    public int getUid() {
        return uid;
    }

    public void setUid(int uid) {
        this.uid = uid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPass() {
        return pass;
    }

    public void setPass(String pass) {
        this.pass = pass;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getCreatetime() {
        return createtime;
    }

    public void setCreatetime(String createtime) {
        this.createtime = createtime;
    }
}

lagou-dubbo-dao(数据访问层工程,jar项目):

在这里插入图片描述

对应的UserMapper接口:
package mapper;

import entity.Users;

/**
 *
 */
public interface UserMapper {
    int register(Users users);
}

对应要添加的依赖:
<dependencies>
        <dependency>
            <groupId>lagou.com</groupId>
            <artifactId>lagou-dubbo-entity</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
对应的UserMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


<mapper namespace="mapper.UserMapper">

    <insert id="register">
        insert into users (username,pass,phone,createtime)
        values (#{username},#{pass},#{phone},#{createtime})

    </insert>

</mapper>
对应的mybatis-config.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 后台的日志输出:针对开发者-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
</configuration>

对应的spring-dao.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:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 1.创建数据连接池对象 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <!--serverTimezone是数据库连接中的参数,用于设置服务时间
		标识设置服务时间为东一区时间,即国际日期变更线时间-->
        <property name="url" value="jdbc:mysql://192.168.164.128:3306/smd?
                                    serverTimezone=GMT&amp;characterEncoding=utf8"/>
        <!--在xml里,若要加上&,则需要&amp;,相当于&-->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="password" value="QiDian@666"/>

    </bean>
    <!-- 2.创建SqlSessionFactory,并引入数据源对象 -->
    <bean id="sqlSessionFactory"
          class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property>

    </bean>
    <!-- 3.告诉spring容器,数据库语句代码在哪个文件中-->
    <!-- mapper.xDao接口对应resources/mapper/xDao.xml-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="mapper"></property>
    </bean>
    <!-- 4.将数据源关联到事务 -->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!-- 5.开启事务 -->
    <tx:annotation-driven/>
</beans>

对应的测试类TestDao类:
package test;

import entity.Users;
import mapper.UserMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring/spring-dao.xml"})
//可以不加{},也可直接写{}里面的,或者不加locations
public class TestDao {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void register(){
        Users u = new Users("a1","11","111","1111");
        int register = userMapper.register(u);
        System.out.println(register);
    }
}

运行测试类,试一试成不成功
lagou-dubbo-interface(服务接口定义工程,jar项目):

在这里插入图片描述

对应要添加的依赖:
    <dependencies>
        <dependency>
            <groupId>lagou.com</groupId>
            <artifactId>lagou-dubbo-dao</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
对应的UserService接口:
package service;

import entity.Users;

/**
 *
 */
public interface UserService {
    int register(Users users);
}

lagou-dubbo-service(privoder服务提供者工程,jar项目):

在这里插入图片描述

对应要添加的依赖:
<!-- <packaging>war</packaging> 在安装之前,这个不要解除注释-->

<dependencies>
        <dependency>
            <groupId>lagou.com</groupId>
            <artifactId>lagou-dubbo-interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <configuration>
                    <port>8001</port>
                    <path>/</path>
                </configuration>
             <!-- <executions>
                    <execution>
                        打包完成后,运行服务 
                        <phase>package</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                </executions>
在安装之前,这个不要解除注释
        -->
            </plugin>
        </plugins>
    </build>
对应的UserServiceImpl类:
package service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import entity.Users;
import mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import service.UserService;

/**
 *
 */
@Service //dubbo的注解,给消费方提供服务,基本只有类全部操作完后,才会暴露,如注入后
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public int register(Users users) {
        return userMapper.register(users);
    }
}

对应的spring-service.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://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://code.alibabatech.com/schema/dubbo
           http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <!--1.服务提供方在zookeeper中的"别名"-->
    <dubbo:application name="lagou-dubbo-server"/>
    <!--2.注册中心的地址-->
    <dubbo:registry address="zookeeper://192.168.164.128:2181"/>
    <!--3.扫描类(将什么包下的类作为服务提供类)-->
    <dubbo:annotation package="service.impl"/>

</beans>
对应的web.xml(这个对测试并没有操作,即可以不写,但写上是为了后面的操作):
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
        http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         id="WebApp_ID" version="3.1">
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:spring/spring-*.xml</param-value>
        <!--需要一起的,下面测试类会进行解释-->
    </context-param>
</web-app>
对应的测试类TestService:
import entity.Users;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import service.UserService;

/**
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:spring/spring-*.xml"})
//通过扫描两个配置文件(这里的全部就是两个,正好得到对应的两个实例,即完整实例,所以可以进行注入了)
//只是这样的操作,idea,却识别不了
//主要是对应的注解,并没有显式的给出实例,这也使得可能打包安装时,会失败,不操作测试即可
//所以下面的注入是提示报错的,实际上运行时不会
public class TestService {

    @Autowired
    private UserService userService;

    @Test
    public void register(){
        Users u = new Users("a2","12","112","1112");
        int register = userService.register(u);
        System.out.println(register);
    }
}

lagou-dubbo-web(consumer服务消费者工程,war项目):

在这里插入图片描述

对应要添加的依赖:
 <packaging>war</packaging>
    <dependencies>
        <dependency>
            <groupId>lagou.com</groupId>
            <artifactId>lagou-dubbo-service</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <configuration>
                    <port>8002</port>
                    <path>/</path>
                </configuration>
                <!-- <executions>
                    <execution>
                        打包完成后,运行服务
                        <phase>package</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                </executions>
                在安装之前,这个不要解除注释
        -->
            </plugin>
        </plugins>
    </build>

对应的UserAction类:
package controller;

import com.alibaba.dubbo.config.annotation.Reference;
import entity.Users;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import service.UserService;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 *
 */
@Controller
public class UserAction {

    @Reference
    private UserService userService;

    @RequestMapping("register")
    @ResponseBody
    public String register(Users users){
        String a = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        users.setCreatetime(a);
        int i = userService.register(users);
        if(i >0){
            return "注册成功";
        }else{
            return "注册失败";
        }
    }

}

对应的spring-mvc.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://code.alibabatech.com/schema/dubbo"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://code.alibabatech.com/schema/dubbo
           http://code.alibabatech.com/schema/dubbo/dubbo.xsd
           http://www.springframework.org/schema/mvc
           http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--1.服务提供方在zookeeper中的"别名"-->
    <dubbo:application name="lagou-dubbo-server"/>
    <!--2.注册中心的地址-->
    <dubbo:registry address="zookeeper://192.168.164.128:2181"/>
    <!--3.扫描类(将什么包下的类作为服务提供类)-->
    <dubbo:annotation package="controller"/>

    <!--由于@ResponseBody注解,在响应对应的数据时,即对应json数据时,对应的Date类型会变成时间戳
    所以我们设置他不变成时间戳返回,即相当于操作了时间戳变成Date类型,即设置如下

虽然我们前面也通过这个,操作对应的格式,但那是请求的操作,这里是响应的操作
    -->
    <!--配置json转换器-->
    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                <property name="supportedMediaTypes" value="application/json"></property>
                <property name="features">
                    <array>
                        <value>WriteMapNullValue</value>
                        <value>WriteDateUseDateFormat</value>
                    </array>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    
    <!--放行静态资源-->
    <mvc:default-servlet-handler/>
</beans>
对应的web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
        http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         id="WebApp_ID" version="3.1">

    <!-- 解决post乱码 -->
    <filter>
        <filter-name>charset</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>

        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
            <!--设置为true,表示被强制使用上面设置的编码(即utf-8),且不可以覆盖
            如request.getCharacterEncoding()方法,就不能进行覆盖了
            默认为false,即可以覆盖-->
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>charset</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/spring-mvc.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>springMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
对应的index.jsp:
<%--
  Created by IntelliJ IDEA.
  User: 33988
  Date: 2022/6/10
  Time: 9:36
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<form>
    <p>username:<input type="text" name="username"></p>
    <p>password:<input type="text" name="pass"></p>
    <!--前面的Users的类是pass,而不是password-->
    <p>phone:<input type="text" name="phone"></p>
    <p><input type="button" id="btn" value="注册"></p>
</form>
<script src="js/jquery-3.4.1.min.js"></script>
<script>
$("#btn").on("click",function () {
    //$("form").serialize()将该表单作为一个对象进行提交
    $.post("register",$("form").serialize(),function (result) {
        alert(result)
    },"json")
})
</script>
</body>
</html>

启动测试:
先选择父项目(聚合工程)进行全员安装成jar包或war包,其中会经过测试(因为按照顺序来的),也就是说
对应测试类会执行(只有对应的测试环境的@Test会进行执行,如@RunWith(SpringJUnit4ClassRunner.class)注解的配置)
而普通的@Test不会,即直接写@Test注解的不会
那么对应数据库里应该会有两个数据(两个测试类执行造成的)
安装完后,对应的pom.xml那里,可以解除注释了,maven的导入是可以导入jar包的,而不能导入war包
所以通常都是web项目来导入其他项目的包,当然了只有对应的包进行覆盖
即war包安装后,那么就算改变成jar包,也不会删除war包,而是添加上jar包,相同的就会覆盖
但在工程之间,可以直接使用(一般只是对maven中,单纯的idea创建的可能不会,因为他不操作pom文件,即并不是操作工程,工程一般有很多操作,使得路径到当前项目,具体工程化的知识可以百度查看),无论是否是jar包还是war包还是没有进行打包都可以直接使用,只是idea的处理,具体部署时,其实也是打包的意思,继续看后面吧
优先当前工程(子到父到子进行获取),只是优先,即无论是否是父子工程都可以,但是在服务器里或者不是当前显示的idea的maven,工程直接就不会互相调用了,即基本都是仓库里操作调用(虽然通常是下载好的,那么按照这样的说法,其实他们是一样的意思,都是拿取下载好的,只不过当前工程忽略了这个过程而已,使得看起来是调用,简单来说,上面只是考虑xml的移动,而没有考虑真实的jar包的本质上的移动,所以无论是当前还是打包,其实都是操作合体的jar包,他们本质是完全一样的),且要注意,父子之间,一般父如果使用子,那么不能打包,且打包不看工程之间,而是看是否安装,这是要注意的
解除注释后:
启动服务service
启动调用者web
进行操作,若返回注册成功,且数据库有数据,则操作成功
最后注意:在dubbo中,服务提供方的方法,最好不要抛出异常,因为会报错,dubbo的操作
但是可以操作RuntimeExcepton异常,会自动的识别,即抛出,所以对应的接口,可以不指定
实际上dubbo就是基本上只识别这个异常,所以其他的异常不能操作
这里在写css要注意一点,自己标签设置的属性一般都会被style覆盖,也可以说是优先级最低
对于端口的占用,如果找不到,可以使用如下操作:
在windows的命令窗口中,输入如下:
netstat -aon|findstr 8080 #找到对应的8080端口,可以直到PID
taskkill /f /PID 6666 #结束指定的PID,这里随便写的一个,就是6666(这个根据你查询的来进行结束)
location.href的优先级比表单提交低,可以说是被覆盖
最后说明以下mybatis:mybatis在不同的版本下,对应String操作不同,在动态sql中,在低版本,mybatis会将String当成类
那么使用动态sql替换时,则是使用get进行获取,而高版本,则当成参数,所有对应的if默认为true(参数基本都是默认true)
而在低版本下,若要使用对应参数,即String的对应值,需要操作_parameter,即String的get得到他的值即可
对于URL中文可能出现乱码问题,主要是浏览器会对中文做特殊处理,而后端却没有对应的解码
我们可以先进行对该中文操作一个新的编码,如使用:
//encodeURI(encodeURI("张三")),进行了两次编码(编成二进制)
//后端使用
//URLDecoder.decode(上面传递的数据, "utf-8"),进行解码即可(操作了两次解码)
//其中就没有中文了,实现了互相传递
//上面的编码和解码不是针对于二进制来说的,是针对中文来说
//上面两个基本只操作中文,其他字母或者数字不会操作,即不会进行编码,相当于没有写这个方法一样
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值