一、项目架构演变过程
随着互联网的发展,用户群体逐渐壮大,网站的流量成倍增长,常规的单体架构已无法满足请求压力暴增和业务的快速迭代,架构的变化势在必行。
1.1、单体架构
单体架构所有模块和功能都集中在一个项目中 ,部署时也是将项目所有功能部整体署到服务器中。如下图:
优点
小项目开发快 成本低
架构简单
易于测试
易于部署
缺点
大项目模块耦合严重 不易开发 维护 沟通成本高
新增业务困难
核心业务与边缘业务混合在一块,出现问题互相影响
1.2、垂直架构
根据业务把项目垂直切割成多个项目,因此这种架构称之为垂直架构。
为了避免上面提到的那些问题,我们开始做模块的垂直划分,做垂直划分的原则是基于拉勾的业务特性,核心目标,第一个是为了业务之间互不影响,第二个是在研发团队的壮大后为了提高效率,减少之间的依赖。
优点
系统拆分实现了流量分担,解决了并发问题
可以针对不同系统进行优化
方便水平扩展,负载均衡,容错率提高
系统间相互独立,互不影响,新的业务迭代时更加高效
缺点
服务系统之间接口调用硬编码
搭建集群之后,实现负载均衡比较复杂
服务系统接口调用监控不到位 调用方式不统一
服务监控不到位
数据库资源浪费,充斥慢查询,主从同步延迟大
1.3、分布式-SOA架构
1.3.1、分布式-SOA介绍
SOA全称为Service Oriented Architecture,即面向服务的架构 。它是在垂直划分的基础上,将每个项目拆分出多个具备松耦合的服务,一个服务通常以独立的形式存在于操作系统进程中。各个服务之间通过网络调用,这使得构建在各种各样的系统中的服务可以 以一种统一和通用的方式进行交互。
我们在做了垂直划分以后,模块随之增多,系统之间的RPC逐渐增多,维护的成本也越来越高,一些通用的业务和模块重复的也越来越多,这个时候上面提到的接口协议不统一、服务无法监控、服务的负载均衡等问题更加突出,为了解决上面的这些问题,我们将通用的业务逻辑下沉到服务层,通过接口暴露,供其他业务场景调用。同时引入了阿里巴巴开源的Dubbo,一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
解释说明:
分层: 按照业务性质分层 每一层要求简单 和 容易维护
应用层:
距离用户最近的一层 也称之为接入层 使用tomcat 作为web容器 接收用户请求 使用下游的dubbo提供的接口来返回数据 并且该层禁止访问数据库
业务服务层:
根据具体的业务场景 演变而来的模块 比如 简历投递 职位搜索 职位推荐等
基础业务层:
拉勾网招聘业务的核心 账号 简历 公司 职位
基础服务层:
这一层 是与业务无关的模块 是一些通用的服务
这类服务的特点:请求量大 逻辑简单 特性明显 功能独立
消息服务(发邮件 短信 微信)
附件解析 50% 自己上传附件简历 需要解析成pdf
存储层:
不同的存储类型 Mysql Mongodb ES fastDFS
分级:按照业务性质分层 同一层的业务也要做好分级 依据业务的重要性进行分级 按照二八定律 网站80%的流量 都在核心功能上面 要优先保证核心业务的稳定。
隔离:不同性质 不同重要性的业务做好隔离 包括 业务 缓存 DB 中间件 都要做好隔离 比如 核心业务的数据库 要和活动相关的数据库隔离
调用 :总体上调用要单向 可以跨层调用 但不能出现逆向调用
1.3.2、分布式-SOA优缺点和微服务
优点
服务以接口为粒度,为开发者屏蔽远程调用底层细节 使用Dubbo 面向接口远程方法调用 屏蔽了底层调用细节
业务分层以后架构更加清晰 并且每个业务模块职责单一 扩展性更强
数据隔离,权限回收,数据访问都通过接口 让系统更加稳定 安全
服务应用本身无状态化 这里的无状态化指的是应用本身不做内存级缓存 而是把数据存入db
服务责任易确定 每个服务可以确定责任人 这样更容易保证服务质量和稳定
缺点
1.粒度控制复杂 如果没有控制好服务的粒度 服务的模块就会越来越多 就会引发 超时 分布式事务等问题
2.服务接口数量不宜控制 容易引发接口爆炸 所以服务接口建议以业务场景进行单位划分 并对相近的业务做抽象 防止接口爆炸
3.版本升级兼容困难 尽量不要删除方法 字段 枚举类型的新增字段也可能不兼容
4.调用链路长 服务质量不可监控 调用链路变长 下游抖动可能会影响到上游业务 最终形成连锁反应 服务质量不稳定 同时链路的变成使得服务质量的监控变得困难
1.4、微服务架构
微服务架构是一种将单个应用程序 作为一套小型服务开发的方法,每种应用程序都在其自己的进程中独立运行,并使用轻量级机制(通常是HTTP资源的API)进行通信。这些服务是围绕业务功能构建的,可以通过全自动部署机制进行独立部署。这些服务的集中化管理非常少,它们可以用不同的编程语言编写,并使用不同的数据存储技术。
微服务是在SOA上做的升华 , 粒度更加细致,微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”。
关于微服务架构的知识,后面Spring Cloud课程中会详细讲解。
二、Dubbo 架构与实战
2.1 Dubbo 架构概述
2.1.1 什么是Dubbo
Apache Dubbo是一款高性能的Java RPC框架。其前身是阿里巴巴公司开源的一个高性能、轻量级的开源Java RPC框架,可以和Spring框架无缝集成。
2.1.2 dubbo 的特性
参考官网首页特性
2.1.3 Dubbo 的服务治理
服务治理(SOA governance),企业为了确保项目顺利完成而实施的过程,包括最佳实践、架构原则、治理规程、规律以及其他决定性的因素。服务治理指的是用来管理SOA的采用和实现的过程。
2.2. Dubbo 处理流程
调用关系说明:
虚线 代表异步调用 实线代表同步访问
蓝色虚线 是在启动时完成的功能
红色虚线 是程序运行中执行的功能
调用流程:
1.服务提供者在服务容器启动时 向注册中心 注册自己提供的服务
2.服务消费者在启动时 向注册中心订阅自己所需的服务
3.注册中心返回服务提供者地址列表给消费者 如果有变更 注册中心会基于长连接推送变更数据给消费者
4.服务消费者 从提供者地址列表中 基于软负载均衡算法 选一台提供者进行调用 如果调用失败 则重新选择一台
5.服务提供者和消费者 在内存中的调用次数 和 调用时间 定时每分钟发送给监控中心
2.3、服务注册中心Zookeeper
通过前面的Dubbo架构图可以看到,Registry(服务注册中心)在其中起着至关重要的作用。Dubbo官
方推荐使用Zookeeper作为服务注册中心。Zookeeper 是 Apache Hadoop 的子项目,作为 Dubbo 服
务的注册中心,工业强度较高,可用于生产环境,并推荐使用 。
Zookeeper的安装及其使用见上一模块,此处不再赘述。
2.4 、Dubbo开发实战
2.4.1 实战案例介绍
在Dubbo中所有的的服务调用都是基于接口去进行双方交互的。双方协定好Dubbo调用中的接口,提供者来提供实现类并且注册到注册中心上
调用方则只需要引入该接口,并且同样注册到相同的注册中心上(消费者)。即可利用注册中心来实现集群感知功能,之后消费者即可对提供者进行调用。
我们所有的项目都是基于Maven去进行创建,这样相互在引用的时候只需要以依赖的形式进行展现就可以了。
并且这里我们会通过maven的父工程来统一依赖的版本。
程序实现分为以下几步骤:
(1)建立maven工程 并且 创建API模块: 用于规范双方接口协定
(2)提供provider模块,引入API模块,并且对其中的服务进行实现。将其注册到注册中心上,对外来统一提供服务。
(3)提供consumer模块,引入API模块,并且引入与提供者相同的注册中心。再进行服务调用。
2.4.2 Annotation 的开发过程
2.4.2.1 接口协定 模块
1.定义maven。
com.lagou
service-api
1.0-SNAPSHOT
2.7.5
org.apache.dubbo
dubbo
${dubbo.version}
org.apache.dubbo
dubbo-common
${dubbo.version}
org.apache.dubbo
dubbo-registry-zookeeper
${dubbo.version}
org.apache.dubbo
dubbo-remoting-api
org.apache.dubbo
dubbo-common
org.apache.dubbo
dubbo-registry-nacos
${dubbo.version}
org.apache.dubbo
dubbo-rpc-dubbo
${dubbo.version}
org.apache.dubbo
dubbo-remoting-api
org.apache.dubbo
dubbo-common
org.apache.dubbo
dubbo-remoting-netty4
${dubbo.version}
org.apache.dubbo
dubbo-remoting-api
org.apache.dubbo
dubbo-serialization-hessian2
${dubbo.version}
org.apache.dubbo
dubbo-common
log4j
log4j
1.2.16
org.slf4j
slf4j-api
1.7.5
org.slf4j
slf4j-log4j12
1.7.5
com.alibaba
fastjson
1.2.62
org.apache.maven.plugins
maven-compiler-plugin
3.3
1.8
2.4.2.2 接口提供者(service-api module)
定义接口,这里为了方便,只是写一个基本的方法。
package com.lagou.service;
public interface HelloService {
String sayHello(String name);
}
2.4.2.3 提供者(service-providermodule)
1.pom文件
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">
duboo-base-annotation
com.lagou
1.0-SNAPSHOT
4.0.0
service-provider
com.lagou
service-api
1.0-SNAPSHOT
org.apache.curator
curator-recipes
4.0.1
org.apache.dubbo
dubbo
org.apache.curator
curator-recipes
4.2.0
org.apache.dubbo
dubbo-registry-zookeeper
org.apache.dubbo
dubbo-rpc-dubbo
org.apache.dubbo
dubbo-remoting-netty4
org.apache.dubbo
dubbo-serialization-hessian2
2.service 的实现
package com.lagou.service.impl;
import com.lagou.service.HelloService;
import org.apache.dubbo.config.annotation.Service;
@Service
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
// try {
// Thread.sleep(3000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
return "hello:"+name;
}
}
3.启动类
package com.lagou;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
public class DubboPureMain {
public static void main(String[] args) throws Exception{
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
context.start();
System.in.read();
}
@Configuration
@EnableDubbo(scanBasePackages = "com.lagou.service.impl")
@PropertySource("dubbo-provider.properties")
static class ProviderConfiguration{
@Bean
public RegistryConfig registryConfig(){
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://127.0.0.1:2181?timeout=10000");
//registryConfig.setTimeout(10000);
return registryConfig;
}
}
}
4.配置
dubbo.application.name=dubbo-demo-annotation-provider
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
2.4.2.4 编写服务消费者
1.pom文件
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">
duboo-base-annotation
com.lagou
1.0-SNAPSHOT
4.0.0
service-commuser
com.lagou
service-api
1.0-SNAPSHOT
org.apache.dubbo
dubbo
org.apache.dubbo
dubbo-registry-zookeeper
org.apache.dubbo
dubbo-rpc-dubbo
org.apache.dubbo
dubbo-remoting-netty4
org.apache.dubbo
dubbo-serialization-hessian2
2.service 的实现
package com.lagou.bean;
import com.lagou.service.HelloService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Component;
@Component
public class ComsumerComponet {
@Reference
private HelloService helloService;
public String sayHello(String name){
return helloService.sayHello(name);
}
}
3.启动类
package com.lagou;
import com.lagou.bean.ComsumerComponet;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import java.io.IOException;
public class AnnotationConsumerMain {
public static void main(String[] args) throws IOException {
System.out.println("------");
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
context.start();
ComsumerComponet service = context.getBean(ComsumerComponet.class);
while (true){
System.in.read();
String hello = service.sayHello("world");
System.out.println("result:"+hello);
}
}
@Configuration
@PropertySource("classpath:/dubbo-consumer.properties")
@ComponentScan(basePackages = "com.lagou.bean" )
@EnableDubbo
static class ConsumerConfiguration{
}
}
2.4.3 xml 的开发过程
2.4.3.1 接口协定 模块
1.定义maven
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">
4.0.0
com.lagou
dubbo-base-xml
pom
1.0-SNAPSHOT
service-api-xml
service-provider-xml
service-conusumer-xml
2.7.5
org.apache.dubbo
dubbo
${dubbo.version}
org.apache.dubbo
dubbo-common
org.apache.dubbo
dubbo-common
${dubbo.version}
org.apache.dubbo
dubbo-registry-zookeeper
${dubbo.version}
org.apache.dubbo
dubbo-remoting-api
org.apache.dubbo
dubbo-common
org.apache.dubbo
dubbo-registry-nacos
${dubbo.version}
org.apache.dubbo
dubbo-rpc-dubbo
${dubbo.version}
org.apache.dubbo
dubbo-remoting-api
org.apache.dubbo
dubbo-common
org.apache.dubbo
dubbo-remoting-netty4
${dubbo.version}
org.apache.dubbo
dubbo-remoting-api
org.apache.dubbo
dubbo-serialization-hessian2
${dubbo.version}
org.apache.dubbo
dubbo-common
log4j
log4j
1.2.16
org.slf4j
slf4j-api
1.7.5
org.slf4j
slf4j-log4j12
1.7.5
com.alibaba
fastjson
1.2.62
org.apache.maven.plugins
maven-compiler-plugin
3.3
1.8
2.4.3.2 接口提供者(service-api-xml module)
定义接口,这里为了方便,只是写一个基本的方法
package com.lagou.service;
public interface HelloService {
String sayHello(String name);
}
2.4.3.3 提供者(service-provider-xml module)
1.pom文件
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">
dubbo-base-xml
com.lagou
1.0-SNAPSHOT
4.0.0
service-provider-xml
com.lagou
service-api
1.0-SNAPSHOT
org.apache.dubbo
dubbo
org.apache.dubbo
dubbo-registry-zookeeper
org.apache.dubbo
dubbo-registry-nacos
org.apache.dubbo
dubbo-rpc-dubbo
org.apache.dubbo
dubbo-remoting-netty4
org.apache.dubbo
dubbo-serialization-hessian2
2.service 的实现
package com.lagou.service.impl;
import com.lagou.service.HelloService;
public class HelloServiceImpl implements HelloService {
public String sayHello(String name) {
return "hello:"+name;
}
}
3.启动类
package com.lagou;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
public class ProviderApplication {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:dubbo-provider.xml");
context.start();
System.in.read();
}
}
4.配置
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo
http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
2.4.3.4 编写服务消费者
1.pom文件
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">
dubbo-base-xml
com.lagou
1.0-SNAPSHOT
4.0.0
service-conusumer-xml
com.lagou
service-api
1.0-SNAPSHOT
org.apache.dubbo
dubbo
org.apache.dubbo
dubbo-common
org.apache.dubbo
dubbo-registry-zookeeper
org.apache.dubbo
dubbo-registry-nacos
org.apache.dubbo
dubbo-rpc-dubbo
org.apache.dubbo
dubbo-remoting-netty4
org.apache.dubbo
dubbo-serialization-hessian2
2.service 的实现
package com.lagou.service.impl;
import com.lagou.service.HelloService;
public class HelloServiceMock implements HelloService {
@Override
public String sayHello(String name) {
return "hello mock";
}
}
3.启动类
package com.lagou.service;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ConsumerApplication {
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("dubbo-comsumer.xml");
classPathXmlApplicationContext.start();
HelloService helloService = classPathXmlApplicationContext.getBean("helloService", HelloService.class);
String result = helloService.sayHello("world");
System.out.println("result="+result);
}
}
4.配置
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
2.5 Dubbo管理控制台 dubbo-admin
5.1 作用
主要包含:服务管理 、 路由规则、动态配置、服务降级、访问控制、权重调整、负载均衡等管理功能
如我们在开发时,需要知道Zookeeper注册中心都注册了哪些服务,有哪些消费者来消费这些服务。我们可以通过部署一个管理中心来实现。其实管理中心就是一个web应用,原来是war(2.6版本以前)包需要部署到tomcat即可。现在是jar包可以直接通过java命令运行。
5.2 控制台安装步骤
1.从git 上下载项目 https://github.com/apache/dubbo-admin
2.修改项目下的dubbo.properties文件 注意dubbo.registry.address对应的值需要对应当前使用的Zookeeper的ip地址和端口号
• dubbo.registry.address=zookeeper://zk所在机器ip:zk端口
• dubbo.admin.root.password=root
• dubbo.admin.guest.password=guest
3.切换到项目所在的路径 使用mvn 打包 mvn clean package -Dmaven.test.skip=true
4.java 命令运行 java -jar 对应的jar包
5.3 使用控制台
1.访问http://IP:端口
2.输入用户名root,密码root
3.点击菜单查看服务提供者和服务消费者信息
6.Dubbo配置项说明
6.1 dubbo:application
对应 org.apache.dubbo.config.ApplicationConfig, 代表当前应用的信息
name: 当前应用程序的名称,在dubbo-admin中我们也可以看到,这个代表这个应用名称。我们在真正时是时也会根据这个参数来进行聚合应用请求。
owner: 当前应用程序的负责人,可以通过这个负责人找到其相关的应用列表,用于快速定位到责任人。
qosEnable : 是否启动QoS 默认true
qosPort : 启动QoS绑定的端口 默认22222
qosAcceptForeignIp: 是否允许远程访问 默认是false
6.2 dubbo:registry
org.apache.dubbo.config.RegistryConfig, 代表该模块所使用的注册中心。一个模块中的服务可以将其注册到多个注册中心上,也可以注册到一个上。后面再service和reference也会引入这个注册中心。
id : 当当前服务中provider或者consumer中存在多个注册中心时,则使用需要增加该配置。在一些公司,会通过业务线的不同选择不同的注册中心,所以一般都会配置该值。
address : 当前注册中心的访问地址。
protocol : 当前注册中心所使用的协议是什么。也可以直接在 address 中写入,比如使用zookeeper,就可以写成 zookeeper://xx.xx.xx.xx:2181 4. timeout : 当与注册中心不再同一个机房时,大多会把该参数延长。
6.3 dubbo:protocol
org.apache.dubbo.config.ProtocolConfig, 指定服务在进行数据传输所使用的协议。
id : 在大公司,可能因为各个部门技术栈不同,所以可能会选择使用不同的协议进行交互。这里在多个协议使用时,需要指定。
name : 指定协议名称。默认使用 dubbo 。
6.4 dubbo:reference
org.apache.dubbo.config.ReferenceConfig, 消费者的配置,这里只做简单说明,后面会具体讲解。
id : 指定该Bean在注册到Spring中的id。 2. interface: 服务接口名
version : 指定当前服务版本,与服务提供者的版本一致。
registry : 指定所具体使用的注册中心地址。这里面也就是使用上面在 dubbo:registry 中所声明的id。
6.5 dubbo:service
org.apache.dubbo.config.ServiceConfig, 用于指定当前需要对外暴露的服务信息,后面也会具体讲解。和 dubbo:reference 大致相同。
interface : 指定当前需要进行对外暴露的接口是什么。
ref : 具体实现对象的引用,一般我们在生产级别都是使用Spring去进行Bean托管的,所以这里面一般也指的是Spring中的BeanId。
version : 对外暴露的版本号。不同的版本号,消费者在消费的时候只会根据固定的版本号进行消费。
6.6 dubbo:method
org.apache.dubbo.config.MethodConfig, 用于在制定的 dubbo:service 或者 dubbo:reference 中的更具体一个层级,指定具体方法级别在进行RPC操作时候的配置,可以理解为对这上面层级中的配置针对于具体方法的特殊处理。
name : 指定方法名称,用于对这个方法名称的RPC调用进行特殊配置。
async: 是否异步 默认false
6.7 dubbo:service和dubbo:reference详解
这两个在dubbo中是我们最为常用的部分,其中有一些我们必然会接触到的属性。并且这里会讲到一些设置上的使用方案。
mock: 用于在方法调用出现错误时,当做服务降级来统一对外返回结果,后面我们也会对这个方法做更多的介绍。
timeout: 用于指定当前方法或者接口中所有方法的超时时间。我们一般都会根据提供者的时长来具体规定。比如我们在进行第三方服务依赖时可能会对接口的时长做放宽,防止第三方服务不稳定导致服务受损。
check: 用于在启动时,检查生产者是否有该服务。我们一般都会将这个值设置为false,不让其进行检查。因为如果出现模块之间循环引用的话,那么则可能会出现相互依赖,都进行check的话,那么这两个服务永远也启动不起来。
retries: 用于指定当前服务在执行时出现错误或者超时时的重试机制。
注意提供者是否有幂等,否则可能出现数据一致性问题
注意提供者是否有类似缓存机制,如出现大面积错误时,可能因为不停重试导致雪崩
executes: 用于在提供者做配置,来确保最大的并行度。
可能导致集群功能无法充分利用或者堵塞
但是也可以启动部分对应用的保护功能
可以不做配置,结合后面的熔断限流使用
6.8 其它配置 参考官网