当网站流量很小时,只需一个应用,将所有功能如下单支付等都部署在一起,以减少部署节点和成本。
缺点:单一的系统架构,使得在开发过程中,占用的资源越来越多,而且随着流量的增加越来越难以维护
垂直应用架构解决了单一应用架构所面临的扩容问题,流量能够分散到各个子系统当中,且系统的体积可控,一定程度上降低了开发人员之间协同以及维护的成本,提升了开发效率。
缺点:但是在垂直架构中相同逻辑代码需要不断的复制,不能复用。
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心。
分布式应用架构,是一种进程间的通信方式。允许像调用本地服务一样调用远程服务。RPC框架负责屏蔽底层的传输方式(如TCP).序列化(如XML/JSON)和通信细节。使用者只需要了解谁在什么位置提供了什么样的远程服务接口即可。
RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。
RPC框架一般包括三个部分:
a)服务提供者
b)服务调用者
c)注册中心
我们需要将服务注册,然后让调用者来发现服务,这需要一个注册中心,来管理所有服务提供者的地址。
随着服务化的进一步发展,服务越来越多,服务之间的调用和依赖关系也越来越复杂,诞生了面向服务的架构体系(SOA),也因此衍生出了一系列相应的技术,如对服务提供、服务调用、连接处理、通信协议、序列化方式、服务发现、服务路由、日志输出等行为进行封装的服务框架。
SOA面向服务的一般原则:服务可复用、松耦合(尽量不要依赖其它独立功能的服务提供者)。服务是底层逻辑的抽象(只有暴露的服务对外可见,底层实现不可见)。服务可以组合编排。
Dubbo是阿里巴巴的一个开源分布式服务框架。它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合)。
从服务模型的角度来看,Dubbo采用的是一种非常简单的模型,要么是提供方提供服务,要么是消费方消费服务,所以基于这一点可以抽象出服务提供方(Provider)和服务消费方(Consumer) 两个角色。
dubbo中主要角色:
a) Provider:生产者,服务提供方
b) Consumer:消费者,调用服务的一方
c) Registry:注册服务和发现服务的地方
d) Monitor:监控中心,统计服务的调用次数和调用时间
它的工作原理是:
1)、轻量级的Java容器通过main函数初始化Spring上下文,根据服务提供者配置的XML文件将服务按照指定的协议发布,完成服务的初始化工作。
2)、服务提供者根据配置的服务注册中心地址连接服务注册中心,将服务提供者信息发布到注册中心。
3)、消费者根据消费者XML配置文件的服务引用信息,连接注册中心,获取指定的服务地址。
4)、服务注册中心根据服务订阅关系,动态地向指定的消费者推送服务地址信息。
5)、消费者调用远程服务时,根据路由策略,从本地缓存的服务提供者地址列表中选择一个服务提供者,然后根据协议类型简历链路,跨进程调用服务提供者。
服务容器负责启动,加载,运行服务提供者。
服务提供者在启动时,向注册中心注册自己提供的服务。
服务消费者在启动时,向注册中心订阅自己所需的服务。
注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
ConfigServer:配置中心,和每个Server/Client之间会作一个实时的心跳检测(因为它们都是建立的Socket长连接),比如几秒钟检测一次。收集每个Server提供的服务的信息,每个Client的信息,整理出一个服务列表,如:
serviceName | serverAddressList | clientAddressList |
---|---|---|
UserService | 192.168.0.1,192.168.0.2,192.168.0.3,192.168.0.4 | 172.16.0.1,172.16.0.2 |
ProductService | 192.168.0.3,192.168.0.4,192.168.0.5,192.168.0.6 | 172.16.0.2,172.16.0.3 |
OrderService | 192.168.0.10,192.168.0.12,192.168.0.5,192.168.0.6 | 172.16.0.3,172.16.0.4 |
当某个Server不可用,那么就更新受影响的服务对应的serverAddressList,即把这个Server从serverAddressList中踢出去(从地址列表中删除),同时将推送serverAddressList给这些受影响的服务的clientAddressList里面的所有Client。如:192.168.0.3挂了,那么UserService和ProductService的serverAddressList都要把192.168.0.3删除掉,同时把新的列表告诉对应的Client 172.16.0.1,172.16.0.2,172.16.0.3;
当某个Client挂了,那么更新受影响的服务对应的clientAddressList
ConfigServer根据服务列表,就能提供一个web管理界面,来查看管理服务的提供者和使用者。
新加一个Server时,由于它会主动与ConfigServer取得联系,而ConfigServer又会将这个信息主动发送给Client,所以新加一个Server时,只需要启动Server,然后几秒钟内,Client就会使用上它提供的服务
Client
调用服务的机器,每个Client启动时,主动与ConfigServer建立Socket长连接,并将自己的IP等相应信息发送给ConfigServer。
Client在使用服务的时候根据服务名称去ConfigServer中获取服务提供者信息(这样ConfigServer就知道某个服务是当前哪几个Client在使用),Client拿到这些服务提供者信息后,与它们都建立连接,后面就可以直接调用服务了,当有多个服务提供者的时候,Client根据一定的规则来进行负载均衡,如轮询,随机,按权重等。
一旦Client使用的服务它对应的服务提供者有变化(服务提供者有新增,删除的情况),ConfigServer就会把最新的服务提供者列表推送给Client,Client就会依据最新的服务提供者列表重新建立连接,新增的提供者建立连接,删除的提供者丢弃连接
Server
真正提供服务的机器,每个Server启动时,主动与ConfigServer建立Scoket长连接,并将自己的IP,提供的服务名称,端口等信息直接发送给ConfigServer,ConfigServer就会收集到每个Server提供的服务的信息。
优点:
1、只要在Client和Server启动的时候,ConfigServer是好的,服务就可调用了,如果后面ConfigServer挂了,那只影响ConfigServer挂了以后服务提供者有变化,而Client还无法感知这一变化。
2、Client每次调用服务是不经过ConfigServer的,Client只是与它建立联系,从它那里获取提供服务者列表而已
3、调用服务-负载均衡:Client调用服务时,可以根据规则在多个服务提供者之间轮流调用服务。
4、服务提供者-容灾:某一个Server挂了,Client依然是可以正确的调用服务的,当前提是这个服务有至少2个服务提供者,Client能很快的感知到服务提供者的变化,并作出相应反应。
5、服务提供者-扩展:添加一个服务提供者很容易,而且Client会很快的感知到它的存在并使用它。
缺点:
只支持java语言
为方便测试,这里采用Maven聚合工程,创建maven工程
1)、创建一个maven工程的子工程provider
2)、引入jar包
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>4.3.11.RELEASEversion>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>dubboartifactId>
<version>2.5.7version>
dependency>
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.4.6version>
dependency>
<dependency>
<groupId>com.github.sgroschupfgroupId>
<artifactId>zkclientartifactId>
<version>0.1version>
dependency>
dependencies>
<build>
<finalName>dubbo-serverfinalName>
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.2version>
<configuration>
<port>8080port>
<path>/path>
configuration>
plugin>
plugins>
build>
2)、创建接口
创建service包,再创建一个接口
package server;
public interface HelloService {
String sayHello();
}
2)、创建接口实现类
实现类实现接口的功能
package server;
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello() {
return "Hell Dubbo";
}
}
3)、发布服务配置
<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">
<bean class="server.HelloServiceImpl" id="helloService"/>
<dubbo:application name="dubbo-server-test" />
<dubbo:registry protocol="zookeeper" address="121.196.62.166:2181"/>
<dubbo:protocol name="dubbo" port="20880"/>
<dubbo:service interface="server.HelloService" ref="helloService" timeout="30000"/>
beans>
4、web.xml配置
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-service.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
web-app>
声明为war工程,配置provider项目的tomcat启动,此时通过ZooKeeper的java连接查询节点,在根节点会多一个dubbo节点
在节点dubbo下遍历存在节点server.HelloService,server.HelloService下继续遍历存在configurators末节点和providers两个节点,
继续遍历providers的节点存在末节点
dubbo%3A%2F%2F169.254.216.55%3A20880%2Fserver.HelloService%3Fanyhost%3Dtrue%26application%3Ddubbo-server-test%26dubbo%3D2.5.7%26generic%3Dfalse%26interface%3Dserver.HelloService%26methods%3DsayHello%26pid%3D912%26side%3Dprovider%26timeout%3D30000%26timestamp%3D1594348569852
节点名里面包含了当前电脑的本地ip和接口全路径server.HelloService,表示当前ip上有个服务对外发布,发布的服务包含的方法
以上的节点名通过UrlDecode解码,可以得到一个dubbo协议的地址信息:
dubbo://169.254.216.55:20880/server.HelloService?anyhost=true&application=dubbo-server-test&dubbo=2.5.7&generic=false&interface=server.HelloService&methods=sayHello&pid=912&side=provider&timeout=30000×tamp=1594348569852
这里走的不是http无法通过浏览器测试,走的是dubbo协议,dubbo协议只能在应用程序进行测试
1)、创建一个maven工程的子工程client
2)、引入jar包,和上面jar包相同
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>4.3.11.RELEASEversion>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>dubboartifactId>
<version>2.5.7version>
dependency>
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.4.6version>
dependency>
<dependency>
<groupId>com.github.sgroschupfgroupId>
<artifactId>zkclientartifactId>
<version>0.1version>
dependency>
dependencies>
3)、编写接口
client客户端请求服务端provider的service,需要实现同一个接口,是为了传输过程中的序列化和反序列化
package server;
public interface HelloService {
String sayHello();
}
4)、spring调用服务
<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">
<dubbo:application name="dubbo-client-test" />
<dubbo:registry protocol="zookeeper" address="121.196.62.166:2181"/>
<dubbo:reference interface="service.HelloService" id="helloService"/>
beans>
5)、测试
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.HelloService;
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
HelloService helloService = applicationContext.getBean(HelloService.class);
System.out.println(helloService.sayHello());
}
}
运行结果:
程序执行过程:此处请求响应走的是网络协议,当通过spring请求helloService方法时,拿到的helloService不是真正的helloService,只是一个代理类,调用helloService的方法时,把调用的过程封装成一个请求,通过网络发送给provider服务端,provider执行真正的任务,执行完把结果返回
中间经历了序列化和反序列化的过程,代理类这边封装的服务通过序列化的形式,把请求封装成一个对象,然后序列化给provider,provider通过反序列化对对象进行解析,解析完后去执行里面的方法,把执行结果再序列化成字节流,通过网络进行传回来。
以上客户端和服务端有重复的接口,造成了代码的冗余,这里可以进行优化,新建一个工程把通用的接口进行封装。
1)、创建一个maven工程的子工程common
2)、创建通用的接口
package server;
public interface HelloService {
String sayHello();
}
3)、pom.xml中声明为jar
<packaging>jar</packaging>
4)、两个工程删除通用接口,pom引入common
<dependency>
<groupId>com.booy.dubbotestgroupId>
<version>1.0-SNAPSHOTversion>
<artifactId>commonartifactId>
dependency>
5)、父工程clean后再install
启动provider服务端,再启动测试类进行测试,效果一样
此时通过ZooKeeper的java连接查询节点service.HelloService里多了consumers和routers两个节点,此时分别查看这两个节点时子节点为空
System.in.read();
将测试类进行阻塞再查看consumers,子节点为:
consumer%3A%2F%2F169.254.216.55%2Fservice.HelloService%3Fapplication%3Ddubbo-client-test%26category%3Dconsumers%26check%3Dfalse%26dubbo%3D2.5.7%26interface%3Dservice.HelloService%26methods%3DsayHello%26pid%3D4716%26side%3Dconsumer%26timestamp%3D1594365797109
节点名里面包含的地址里当前电脑的本地ip和服务的相关信息
和上面服务端一样,以上的节点名通过UrlDecode解码,可以得到一个dubbo协议的地址信息:
consumer://169.254.216.55/service.HelloService?application=dubbo-client-test&category=consumers&check=false&dubbo=2.5.7&interface=service.HelloService&methods=sayHello&pid=4716&side=consumer×tamp=1594365797109
第一个tomcat默认8080端口,再重启一个8088端口,修改暴露端口号和输出内容,模拟跑两个服务
此时再运行测试方法,会随机调用输出两个服务端的方法内容
此时通过ZooKeeper的java连接查询节点/dubbo/service.HelloService/providers可以看到两个服务端节点,当停止一个服务,自然providers节点只剩下一个。
1)、准备dubbo-admin.war的war包
方式一:感谢某位大神网上分享的war包,链接:https://pan.baidu.com/s/1V4x1BhD-jZ0Y8Aws-gxAOQ 密码:ayia
方式二:github下载打包:https://github.com/apache/dubbo-admin
2)、此处直接用方式一,下载后是一个war包,直接放到tomcat安装目录的webapps目录下,然后启动tomcat即可
浏览器访问地址:http://localhost:8088/dubbo-admin/,默认账号和密码都是root
可以看到统计里存在的两个服务者和一个消费者
SpringBoot2.0和SpringBoot1.5整合dubbo的时候,命名差别较大。这里使用的是SpringBoot2.04
实例工程,为了简化,在一个pom工程中加了三个model
springboot-dubbodemo是pom工程,定义了子工程需要引入的jar
boot-service: jar 工程,要发布出去的服务接口
boot-provider: jar工程,服务提供者,接口实现类
boot-consumer: jar工程,服务调用者,controller
springboot-dubbodemo的引入jar包
<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.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.2.RELEASEversion>
<relativePath/>
parent>
<groupId>com.booy.springboot-dubbogroupId>
<artifactId>springboot-dubbodemoartifactId>
<packaging>pompackaging>
<version>1.0-SNAPSHOTversion>
<modules>
<module>boot-servicemodule>
<module>boot-providermodule>
<module>boot-consumermodule>
modules>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
<dubbo-springboot.version>0.2.0dubbo-springboot.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.bootgroupId>
<artifactId>dubbo-spring-boot-starterartifactId>
<version>${dubbo-springboot.version}version>
dependency>
dependencies>
dependencyManagement>
project>
1)、创建HelloService接口
package com.booy.dubbo.service;
public interface HelloService {
String sayHello(String name);
}
1)、引入jar包
<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>com.booy.springboot-dubbogroupId>
<artifactId>springboot-dubbodemoartifactId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>boot-providerartifactId>
<dependencies>
<dependency>
<groupId>com.booy.springboot-dubbogroupId>
<artifactId>boot-serviceartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibaba.bootgroupId>
<artifactId>dubbo-spring-boot-starterartifactId>
dependency>
dependencies>
project>
2)、实现接口
package com.booy.dubbo.provider;
import com.alibaba.dubbo.config.annotation.Service;
import com.booy.dubbo.service.HelloService;
@Service
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "Hello"+name;
}
}
3)、启动类
package com.booy.dubbo.provider;
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubbo
public class ProviderStarter {
public static void main(String[] args) {
SpringApplication.run(ProviderStarter.class);
}
}
4)、配置文件
dubbo:
application:
name: boot-provider
# 协议
protocol:
name: dubbo
port: 20880
registry:
address: zookeeper://121.196.62.166:2181
scan:
base-packages: com.booy.dubbo.provider
server:
port: 8080
1)、引入jar包
<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>com.booy.springboot-dubbogroupId>
<artifactId>springboot-dubbodemoartifactId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>boot-consumerartifactId>
<dependencies>
<dependency>
<groupId>com.booy.springboot-dubbogroupId>
<artifactId>boot-serviceartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibaba.bootgroupId>
<artifactId>dubbo-spring-boot-starterartifactId>
dependency>
dependencies>
project>
2)、controller服务调用
package com.booy.dubbo.consumer;
import com.alibaba.dubbo.config.annotation.Reference;
import com.booy.dubbo.service.HelloService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
//这里返回的是dubbo里的对象
@Reference
private HelloService helloService;
@RequestMapping("hello")
public String hello(String name){
return helloService.sayHello(name);
}
}
3、启动类
package com.booy.dubbo.consumer;
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubbo
public class ConsumerStarter {
public static void main(String[] args) {
SpringApplication.run(ConsumerStarter.class);
}
}
4)、配置文件
dubbo:
application:
name: boot-consumer
registry:
address: zookeeper://121.196.62.166:2181
consumer:
timeout: 5000
server:
port: 8082