分布式系统架构+RPC+Dubbo
分布式系统架构
分布式架构概述
分布式系统是建立在网络之上的软件系统,相对于一个系统只由一个程序组成的单体程序而言,分布式程序是由多个子系统程序组成的,并且这些子程序是相互联结且互相协作的.
分布式架构与单体架构的比较
单体
单体架构就是一个项目包含了整个项目的全部代码,包含了全部功能,通过包(package)区分各个模块
优点:部署简单,维护方便,成本低
缺点:当项目规模大、用户访问频率高、并发量大、数据量大时,会大大降低程序执行效率,甚至出现服务器宕机等情况。
适用项目:传统管理项目,小型互联网项目
分布式
分布式架构会把一个项目按照特定要求(多按照模块或功能)拆分成多个项目,每个项目分别部署到不同的服务器上。
优点:
1.增大了系统可用性。减少单点故障,导致整个应用不可用。
2.增加重用性。因为模块化,所以重用性更高。高内聚、低耦合。
3.增加可扩展性。有新的模块增加新的项目即可。
4.增加每个模块的负载能力。因为每个模块都是一个项目,所以每个模块的负载能力更强。
缺点:
1.成本更高。
2.架构更加复杂。
3.整体响应之间变长,一些业务需要多项目通信后给出结果。
4.吞吐量更大。吞吐量= 请求数/秒。
适用项目:中、大型互联网项目。客户多,数据多,访问并发高,压力大,吞吐量高.
问:分布式系统下,子项目之间如何互相协作?
答:RPC
RPC
PRC的概述
RPC:远程过程调用,在java中,就是调用其他服务器上部署的应用的方法.所以,在分布式架构下,分布在不同的服务器上的子节点程序之间,相互调用方法时,需要使用RPC来完成远程方法调用.
RPC架构组成
典型 RPC 的使用场景中,包含了服务发现、负载、容错、网络传输、序列化等组件,其中“RPC 协议”就指明了程序如何进行网络传输和序列化。
一次完整的RPC过程
1.客户端以本地调用的方式(接口的方式)发出服务请求
2.客户端存根收到调用的请求后,把方法、参数等组装成一个消息体(序列化成二进制)
3.消息体通过socket的方式从客户端socket发送到服务端socket
4.服务端socket收到消息体后,经过服务端存根解码(反序列化)
5.服务端存根将参数传递给服务端方法,并执行被调用的方法
6-10.执行完成后再把返回结果以同样的方式发回给客户端,
总结:RPC的目的就是把234789这些步骤封装起来.
问:在java实现RPC的方式?
答:dubbo
dubbo
dubbo的概述
dubbo是一款高性能的java RPC框架,作为RPC框架,提高性能的关键在于序列化和网络通信两个方面
dubbo的序列化
本地的对象要在网络上进行传输,必须序列化(实现Serializable接口),常用的序列化类型有java原生、xml、json、Hessian和二进制等,效率最高的就是二进制流(因为计算机就是二进制的),dubbo采用的就是二进制流.
dubbo的网络通信
相较于HTTP的7步走(三次握手,四次挥手),dubbo采用socket通信一步到位,提升了通信效率,并且支持长连接,不用反复连接直接传输数据
dubbo的特点
1.面向接口代理,为开发者屏蔽远程调用的底层细节,使用简单
2.多种负载均衡策略,智能负载均衡,减少调用延迟,提高系统吞吐量
3.支持多种注册中心服务,服务自动注册,实时感知服务上下线
4.运行期流量调度,支持灰度发布
5.高可扩展能力,平等对待内部实现和第三方实现
6.监控中心能够提供服务调用统计功能,提供可视化的管理和维护功能
dubbo中的角色
什么是服务提供者/消费者
远程调用过程必然有服务提供者和消费者,比如订单服务器提供订单服务,用户服务器使用订单服务,其中订单服务器是提供者,用户服务器是消费者.反之亦然.
所以在RPC中没有绝对的消费者和提供者,任何服务都能同时兼备消费者和提供者两个身份,并且绝大多数时候如此
什么是注册中心
假如我们有100台A服务器和100台B服务器,其中一台A想要调用B,这时候A怎么知道要调用哪一台B(总不可能每台A上都存储所有B的信息吧?)
这时候,我们就需要类似于婚介所的角色,这个地方存储了所有的信息(包含A和B),小伙找小姑娘可以随时问婚介所,反之亦然
实际注册中心就能根据负载策略来分配调用哪一台服务器,dubbo推荐使用 Zookeeper 注册中心
dubbo与springboot整合案例
0.安装并启动zookeeper
1.创建maven项目dubbodemo作为父项目
2.创建maven项目名api ,添加依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
创建提供方和消费方都需要调用的公共pojo和接口
package cn.ppl.api.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private Integer id;
private String name;
private Integer age;
}
package cn.ppl.api.service;
import cn.ppl.api.pojo.User;
public interface ProviderService {
User setUser(User user);
}
3.创建服务提供方,springboot项目名provider,添加依赖
<!-- 引入spring-boot-starter以及dubbo和curator的依赖 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.3</version>
</dependency>
<!-- 下面两个是dubbo的依赖包-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.2.0</version>
</dependency>
<!-- 公共api依赖包-->
<dependency>
<groupId>cn.ppl</groupId>
<artifactId>api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
创建将要被远程调用的service
注意:@service注解是dubbo的,不是spring的
package cn.ppl.provider.service.impl;
import cn.ppl.api.pojo.User;
import cn.ppl.api.service.ProviderService;
import org.apache.dubbo.config.annotation.Service;
@Service
public class ProviderServiceImpl implements ProviderService {
@Override
public User setUser(User user){
user.setId(11);
user.setAge(16);
user.setName("Jhon");
return user;
}
}
配置文件配置dubbo
#命名本服务
dubbo.application.name=provider
#声明注册中心
dubbo.registry.address=zookeeper://192.168.134.100:2181
4.创建springboot项目名consumer,添加依赖(比provider多个web),
<!-- 引入spring-boot-starter以及dubbo和curator的依赖 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.3</version>
</dependency>
<!-- 下面两个是dubbo的依赖包-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.2.0</version>
</dependency>
<!-- 公共api依赖包-->
<dependency>
<groupId>cn.ppl</groupId>
<artifactId>api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
创建controller和service
package cn.ppl.consumer.controller;
import cn.ppl.api.pojo.User;
import cn.ppl.consumer.service.ConsumerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UserController {
@Autowired
ConsumerService consumerService;
@RequestMapping("/demo")
@ResponseBody
public String demo(){
User user = new User();
return consumerService.showUser(user);
}
}
package cn.ppl.consumer.service.impl;
import cn.ppl.api.pojo.User;
import cn.ppl.api.service.ProviderService;
import cn.ppl.consumer.service.ConsumerService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;
@Service
public class ConsumerServiceImpl implements ConsumerService {
@Reference
private ProviderService providerService;
@Override
public String showUser(User user){
return providerService.setUser(user).toString();
}
}
配置文件配置dubbo
#避免和监控中心端口冲突,设为8082端口访问
server.port=8082
#命名本服务
dubbo.application.name=consumer
#声明注册中心
dubbo.registry.address=zookeeper://192.168.134.100:2181
总结:把spring的@Service和@Autowired替换成dubbo的@Service和@Reference ,配置服务名和注册中心 ,其他部分代码不变