【SpringCloud Alibaba系列】Dubbo高级特性篇

本文涉及的产品
云原生网关 MSE Higress,422元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
注册配置 MSE Nacos/ZooKeeper,118元/月
简介: 本章我们介绍Dubbo的常用高级特性,包括序列化、地址缓存、超时与重试机制、多版本、负载均衡。集群容错、服务降级等。

Dubbo-高级特性篇


一、序列化

Java对象 -> 序列化 -> 流数据

流数据 -> 反序列化 -> Java对象

依赖一个公共的相同模块即保证序列化版本号在序列和反序列时相同。

  • dubbo内部已经将序列化和反序列化的过程内部封装了
  • 我们只需要在定义pojo类时实现serializable接口即可
  • 一般会定义一个公共的pojo模块,让生产者和消费者都依赖该模块。内部已经将序列化和反序列化的过程内部封装了我们只需要在定义类时实现可序列化的接口即可一般会定义一个公共的POJO模块,让生产者和消费者都依赖该模块。

1)创建一个公共实体类依赖

public class User {
    private int id;
    private String username;
    private String password;
    public User() {
    }
    public User(int id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

2)在dubbo-interface里去添加一个查询用户的接口,首先需要引入dubbo-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">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.itheima</groupId>
    <artifactId>dubbo-interface</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.itheima</groupId>
            <artifactId>dubbo-pojo</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>
import com.itheima.pojo.User;
public interface UserService {
    String sayHello();
    /**
     * 查询用户
     */
    User findUserById(int id);
}

3)在dubbo-service中实现findUserById方法

import com.itheima.pojo.User;
import com.itheima.service.UserService;
import org.apache.dubbo.config.annotation.Service;
// 将这个类提供的方法(服务)对外发布。将访问地址(ip、端口、访问路径)注册到ZK注册中心
@Service
public class UserServiceImpl implements UserService {
    public String sayHello() {
        return "Hello Dubbo RPC Zookeeper!~";
    }
    @Override
    public User findUserById(int id) {
        User user = new User(1, "zhangsan", "123");
        return user;
    }
}

4)在dubbo-web中实现调用服务消费

import com.itheima.pojo.User;
import com.itheima.service.UserService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
    @Reference
    private UserService userService;
    @RequestMapping("/sayHello")
    public String sayHello() {
        return userService.sayHello();
    }
    /**
     * 根据id查询用户信息
     * @param id
     * @return
     */
    @RequestMapping("/find")
    public User find(int id) {
        return userService.findUserById(id);
    }
}

此时interface模块依赖pojo模块,web模块依赖service模块、web和service模块依赖interface模块。interface和pojo上都有修改,都需要进行mvn clean install。

重启dubbo-service和dubbo-web服务,访问http://localhost:8000/user/find.do?id=1

这个原因写的很明确,就是pojo实体类上需要实现Serializable接口,因为Java对象的数据需要在不同的机器上通过流来传输的。

5)所有的实体类都实现Serializable接口

import java.io.Serializable;
/*
    注意!!!
    将来所有的pojo类都需要实现serializable接口
 */
public class User implements Serializable {
    private int id;
    private String username;
    private String password;
    public User() {
    }
    public User(int id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

注意:被依赖的Java工程改动后,一定要install重新安装。

6)对修改的服务进行clean install重新安装,重启相关依赖服务,重新访问


二、地址缓存

面试题:注册中心挂了,服务是否能正常访问?

  • 可以,因为dubbo服务消费者在第一次调用时,会从注册中心中获取服务提供者的地址,然后进行访问,之后会将服务提供方地址缓存到本地,以后在调用则不会访问注册中心,直接从本地缓存中去获取,不用再跟注册中心去交互。
  • 当服务提供者地址发生变化时,注册中心会通知服务消费者需要缓存更新。

我们可以进行测试一下,将Linux服务器上的ZK关掉。

再次访问老的服务地址,是可以访问的。但是如果注册中心挂了,新的服务就无法注册了,还是需要进行修复的。


三、超时与重试机制

1. 超时机制

  • 服务消费者在调用服务提供者的时候发生了阻塞、等待的情形,这个时候,服务消费者会一直等待下去。
  • 在某个峰值时刻,大量的请求都在同时请求服务消费者,会造成线程的大量堆积,当服务器资源耗尽,势必会造成雪崩。

  • dubbo利用超时机制来解决这个问题,设置一个超时时间,在这个时间段内,无法完成服务访问,则自动断开连接,将线程释放,这样就不会造成积压导致服务雪崩。
  • 使用timeout属性配置超时时间,默认值1000,单位毫秒。

我们来模拟一下超时情况。在@Service中配置timeout超时时间)、retries超时后重试的次数(默认2次)。

import com.itheima.pojo.User;
import com.itheima.service.UserService;
import org.apache.dubbo.config.annotation.Service;
// 将这个类提供的方法(服务)对外发布。将访问地址(ip、端口、访问路径)注册到ZK注册中心
@Service(timeout = 3000, retries = 0)   // 当前服务3秒超时,超时后,重试0次
public class UserServiceImpl implements UserService {
    public String sayHello() {
        return "Hello Dubbo RPC Zookeeper!~";
    }
    @Override
    public User findUserById(int id) {
        User user = new User(1, "zhangsan", "123");
        // 模拟数据库查询耗时5秒
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return user;
    }
}

在Controller服务调用方模拟线程异步调用1秒打印一个数字。

import com.itheima.pojo.User;
import com.itheima.service.UserService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
    @Reference
    private UserService userService;
    @RequestMapping("/sayHello")
    public String sayHello() {
        return userService.sayHello();
    }
    /**
     * 根据id查询用户信息
     * @param id
     * @return
     */
    int i = 1;
    @RequestMapping("/find")
    public User find(int id) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println(i++);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        return userService.findUserById(id);
    }
}

重启服务,访问测试

发现打印了4个数字然后报错timeout,第4个数字是因为service的线程睡眠时间影响,所以大概可以认为用了3秒触发超时。

@Reference(timeout = 1000)
private UserService userService;

注意:如果两边都配置了服务超时时间,那么consumer优先provider,服务调用方的超时时间会覆盖服务提供方。

建议:在服务提供者上配置超时时间,因为定义服务的人写具体service逻辑时才能估计服务耗时多久。

2. 重试机制

  • 设置了超时时间,在这个时间段内,无法完成服务访问,则自动断开连接。
  • 如果出现网络抖动,则这一次请求就会失败。
  • Dubbo提供重试机制来避免类似问题的发生。
  • 通过retries属性来设置重试次数。默认为2次。
import com.itheima.pojo.User;
import com.itheima.service.UserService;
import org.apache.dubbo.config.annotation.Service;
// 将这个类提供的方法(服务)对外发布。将访问地址(ip、端口、访问路径)注册到ZK注册中心
@Service(timeout = 3000, retries = 2)   // 当前服务3秒超时,超时后,重试2次,一共3次
public class UserServiceImpl implements UserService {
    public String sayHello() {
        return "Hello Dubbo RPC Zookeeper!~";
    }
    int i = 1;
    @Override
    public User findUserById(int id) {
        System.out.println("该服务一共被调用:" + i++ + "次。");
        User user = new User(1, "zhangsan", "123");
        // 模拟数据库查询耗时5秒
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return user;
    }
}


四、多版本

  • 灰度发布:当出现新功能时,会让一部分用户先使用新功能,用户反馈没问题时,再将所有用户迁移到新功能。

  • dubbo中使用version属性来设置和调用同一个接口不同版本
import com.itheima.pojo.User;
import com.itheima.service.UserService;
import org.apache.dubbo.config.annotation.Service;
@Service(version = "v1.0")   // 指定当前服务的版本
public class UserServiceImpl implements UserService {
    public String sayHello() {
        return "Hello Dubbo RPC Zookeeper!~";
    }
    @Override
    public User findUserById(int id) {
        System.out.println("old version is v1.0");
        User user = new User(1, "zhangsan", "123");
        return user;
    }
}
import com.itheima.pojo.User;
import com.itheima.service.UserService;
import org.apache.dubbo.config.annotation.Service;
@Service(version = "v2.0")   // 指定当前服务的版本
public class UserServiceImpl2 implements UserService {
    public String sayHello() {
        return "Hello Dubbo RPC Zookeeper!~";
    }
    @Override
    public User findUserById(int id) {
        System.out.println("new version is v2.0");
        User user = new User(2, "lisi", "456");
        return user;
    }
}

web服务消费者注入指定版本的service。

@Reference(version = "v1.0")    // 注入指定版本的service
private UserService userService;

修改web服务注入为v2.0,重启web服务。

@Reference(version = "v2.0")    // 注入指定版本的service
private UserService userService;


五、负载均衡

负载均衡策略(4种)

  • Random:按权重随机,默认值。按权重设置随机概率。
  • RoundRobin:按权重轮询。
  • LeastActive:最少活跃调用数,相同活跃数的随机。
  • ConsistentHash:一致性Hash,相同参数的请求总是发到同一提供者。

Dubbo官方文档:https://cn.dubbo.apache.org/zh-cn/overview/core-features/load-balance/

1. Random

Random:按权重随机,默认值。按权重设置随机概率。

负载均衡是在服务器集群部署环境下操作的,我们可以让同一个服务启动三次来模拟多台服务器集群。但是需要注意:端口需要改变,否则会冲突。

服务器配置如下,每修改完一次端口启动一个服务。

  • dubbo-service服务器1:
  • tomcat插件的端口:9000
  • <dubbo:protocol port="20880"/> 默认值为20880
  • <dubbo:parameter key="qos.port" value="22222"/> qos.port默认值为22222
  • @Service(weight = 100)
  • dubbo-service服务器2:
  • tomcat插件的端口:9002
  • <dubbo:protocol port="20882"/>
  • <dubbo:parameter key="qos.port" value="44444"/>
  • @Service(weight = 200)
  • dubbo-service服务器3:
  • tomcat插件的端口:9003
  • <dubbo:protocol port="20883"/>
  • <dubbo:parameter key="qos.port" value="55555"/>
  • @Service(weight = 100)
  • dubbo-web服务:
  • tomcat插件的端口:8000
  • <dubbo:parameter key="qos.port" value="33333"/>
  • @Reference(loadbalance = "random")
import com.itheima.pojo.User;
import com.itheima.service.UserService;
import org.apache.dubbo.config.annotation.Service;
@Service(weight = 100)
public class UserServiceImpl implements UserService {
    public String sayHello() {
        return "1.....";
    }
    @Override
    public User findUserById(int id) {
        User user = new User(1, "zhangsan", "123");
        return user;
    }
}
import com.itheima.pojo.User;
import com.itheima.service.UserService;
import org.apache.dubbo.config.annotation.Service;
@Service(weight = 200)
public class UserServiceImpl implements UserService {
    public String sayHello() {
        return "2.....";
    }
    @Override
    public User findUserById(int id) {
        User user = new User(1, "zhangsan", "123");
        return user;
    }
}
import com.itheima.pojo.User;
import com.itheima.service.UserService;
import org.apache.dubbo.config.annotation.Service;
@Service(weight = 100)
public class UserServiceImpl implements UserService {
    public String sayHello() {
        return "3.....";
    }
    @Override
    public User findUserById(int id) {
        User user = new User(1, "zhangsan", "123");
        return user;
    }
}

在消费者配置接口负载均衡策略,4种策略名称可以通过搜索AbstractLoadBalance的4种实现类中定义的name值查看。

@Reference(loadbalance = "random")  // 设置负载均衡策略
private UserService userService;

可能会遇到的问题:maven无法下载源码

通过IDEA进行下载源码,点进源码的.class文件,IDE会自动弹出Download Sources or Choose Sources当点击Download Sources时,会报如下错误:

Cannot download sources Sources not found for: org.apache.xxx

解决方法:通过命令行终端进入到工程文件目录里(pom.xml文件所在目录),执行如下命令:

mvn dependency:resolve -Dclassifier=sources

接下来他就会下载jar和源码了

通过权重配比,访问后可以发现,服务器1、2、3的访问概率大概为25%、50%、25%,实现了第一种Random按权重随机的负载均衡。

2. RoundRobin

RoundRobin:按权重轮询。

当weight分别为100、200、100时,访问4次的顺序可能为1,2,3,2。

3. LeastActive

LeastActive:最少活跃调用数,相同活跃数的随机。

每个服务维护一个活跃数计数器。当A机器开始处理请求,该计数器加1,此时A还未处理完成。若处理完毕则计数器减1。而B机器接受到请求后很快处理完毕。那么A,B的活跃数分别是1,0。当又产生了一个新的请求,则选择B机器去执行(B活跃数最小),这样使慢的机器A收到少的请求。

4. ConsistentHash

ConsistentHash:—致性Hash,相同参数的请求总是发到同一提供者。


六、集群容错

集群容错模式:

  • Failover Cluster:失败重试。默认值。当出现失败,重试其它服务器,默认重试2次,使用retries配置。一般用于读操作。
  • Failfast Cluster:快速失败,只发起一次调用,失败立即报错,不重试。通常用于写操作,因为写操作是非幂等性的,可能会造成数据库的数据产生错误情况。
  • Failsafe Cluster:失败安全,出现异常时,直接忽略。返回一个空结果,一般用于写一些不太重要的操作,如日志。
  • FailbackCluster:失败自动恢复,后台记录失败请求,定时重发,直到成功。重要操作可以用这个模式。
  • Forking Cluster:并行调用多个服务器,只要一个成功即返回。但是这个模式性能比较低。
  • Broadcast Cluster : 广播调用所有提供者,逐个调用,任意一台报错则报错。适用于服务之间同步性要求高的,需要保持都一致。

这里演示第一种集群容错模式Failover Cluster失败重试。

和之前的启动设置一样,也是设置不同端口启动三台服务器。

默认超时时间timeout为1秒,重试次数为2次。这里让服务器1和2模拟超时情况,服务器3上的服务是不超时的。

import com.itheima.pojo.User;
import com.itheima.service.UserService;
import org.apache.dubbo.config.annotation.Service;
@Service
public class UserServiceImpl implements UserService {
    public String sayHello() {
        return "hello dubbo";
    }
    @Override
    public User findUserById(int id) {
        System.out.println("1.....");
        User user = new User(1, "zhangsan", "123");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return user;
    }
}
import com.itheima.pojo.User;
import com.itheima.service.UserService;
import org.apache.dubbo.config.annotation.Service;
@Service
public class UserServiceImpl implements UserService {
    public String sayHello() {
        return "hello dubbo";
    }
    @Override
    public User findUserById(int id) {
        System.out.println("2.....");
        User user = new User(1, "zhangsan", "123");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return user;
    }
}
import com.itheima.pojo.User;
import com.itheima.service.UserService;
import org.apache.dubbo.config.annotation.Service;
@Service
public class UserServiceImpl implements UserService {
    public String sayHello() {
        return "hello dubbo";
    }
    @Override
    public User findUserById(int id) {
        System.out.println("3.....");
        User user = new User(1, "zhangsan", "123");
        return user;
    }
}

搜索Cluster的实现类FailoverCluster,

import com.itheima.pojo.User;
import com.itheima.service.UserService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
    // 配置集群容错模式为failover失败重试(默认)
    @Reference(cluster = "failover")
    private UserService userService;
    @RequestMapping("/sayHello")
    public String sayHello() {
        return userService.sayHello();
    }
    /**
     * 根据id查询用户信息
     * @param id
     * @return
     */
    @RequestMapping("/find")
    public User find(int id) {
        return userService.findUserById(id);
    }
}

启动三个服务器和web服务,测试访问。

服务器1超时,服务器2重试。

服务器2超时,服务器3重试。

服务器3未超时执行完毕,返回查询数据。

其中中间过程web服务会报一个远程调用超时错误。


七、服务降级

在并发量较高的情况下,服务器的资源将要被跑满,此时就可以关闭掉一些不重要的服务,比如为了让支付服务不受到影响,可选择关闭广告与日志服务,释放掉一些资源,降低服务器压力,即对服务进行降级处理。

服务降级方式:

  • mock=force:return null表示消费方对该服务的方法调用都直接返回null值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
  • mock=fail:return null表示消费方对该服务的方法调用在失败后,再返回null值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
@Reference(mock = "force:return null")    // 暴力返回null,不再调用userService服务
private UserService userService;
@Reference(mock = "fail:return null")    // 失败后返回null,如服务超时报错
private UserService userService;
相关实践学习
基于MSE实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
相关文章
|
3天前
|
存储 SpringCloudAlibaba Java
【SpringCloud Alibaba系列】一文全面解析Zookeeper安装、常用命令、JavaAPI操作、Watch事件监听、分布式锁、集群搭建、核心理论
一文全面解析Zookeeper安装、常用命令、JavaAPI操作、Watch事件监听、分布式锁、集群搭建、核心理论。
【SpringCloud Alibaba系列】一文全面解析Zookeeper安装、常用命令、JavaAPI操作、Watch事件监听、分布式锁、集群搭建、核心理论
|
3天前
|
SpringCloudAlibaba JavaScript Dubbo
【SpringCloud Alibaba系列】Dubbo dubbo-admin安装教程篇
本文介绍了 Dubbo-Admin 的安装和使用步骤。Dubbo-Admin 是一个前后端分离的项目,前端基于 Vue,后端基于 Spring Boot。安装前需确保开发环境(Windows 10)已安装 JDK、Maven 和 Node.js,并在 Linux CentOS 7 上部署 Zookeeper 作为注册中心。
【SpringCloud Alibaba系列】Dubbo dubbo-admin安装教程篇
|
3天前
|
SpringCloudAlibaba Dubbo Java
【SpringCloud Alibaba系列】Dubbo基础入门篇
Dubbo是一款高性能、轻量级的开源Java RPC框架,提供面向接口代理的高性能RPC调用、智能负载均衡、服务自动注册和发现、运行期流量调度、可视化服务治理和运维等功能。
【SpringCloud Alibaba系列】Dubbo基础入门篇
|
7月前
|
Dubbo Java 应用服务中间件
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
|
2月前
|
Dubbo Java 应用服务中间件
Spring Cloud Dubbo:微服务通信的高效解决方案
【10月更文挑战第15天】随着信息技术的发展,微服务架构成为企业应用开发的主流。Spring Cloud Dubbo结合了Dubbo的高性能RPC和Spring Cloud的生态系统,提供高效、稳定的微服务通信解决方案。它支持多种通信协议,具备服务注册与发现、负载均衡及容错机制,简化了服务调用的复杂性,使开发者能更专注于业务逻辑的实现。
75 2
|
4月前
|
Dubbo Java 应用服务中间件
💥Spring Cloud Dubbo火爆来袭!微服务通信的终极利器,你知道它有多强大吗?🔥
【8月更文挑战第29天】随着信息技术的发展,微服务架构成为企业应用开发的主流模式,而高效的微服务通信至关重要。Spring Cloud Dubbo通过整合Dubbo与Spring Cloud的优势,提供高性能RPC通信及丰富的生态支持,包括服务注册与发现、负载均衡和容错机制等,简化了服务调用管理并支持多种通信协议,提升了系统的可伸缩性和稳定性,成为微服务通信领域的优选方案。开发者仅需关注业务逻辑,而无需过多关心底层通信细节,使得Spring Cloud Dubbo在未来微服务开发中将更加受到青睐。
90 0
|
1月前
|
Dubbo Cloud Native 应用服务中间件
阿里云的 Dubbo 和 Nacos 深度整合,提供了高效的服务注册与发现、配置管理等关键功能,简化了微服务治理,提升了系统的灵活性和可靠性。
在云原生时代,微服务架构成为主流。阿里云的 Dubbo 和 Nacos 深度整合,提供了高效的服务注册与发现、配置管理等关键功能,简化了微服务治理,提升了系统的灵活性和可靠性。示例代码展示了如何在项目中实现两者的整合,通过 Nacos 动态调整服务状态和配置,适应多变的业务需求。
43 2
|
2月前
|
Dubbo Java 应用服务中间件
Dubbo学习圣经:从入门到精通 Dubbo3.0 + SpringCloud Alibaba 微服务基础框架
尼恩团队的15大技术圣经,旨在帮助开发者系统化、体系化地掌握核心技术,提升技术实力,从而在面试和工作中脱颖而出。本文介绍了如何使用Dubbo3.0与Spring Cloud Gateway进行整合,解决传统Dubbo架构缺乏HTTP入口的问题,实现高性能的微服务网关。
|
3月前
|
Dubbo 应用服务中间件 Apache
Star 4w+,Apache Dubbo 3.3 全新发布,Triple X 领衔,开启微服务通信新时代
在 Apache Dubbo 突破 4w Star 之际,Apache Dubbo 团队正式宣布,Dubbo 3.3 正式发布!作为全球领先的开源微服务框架,Dubbo 一直致力于为开发者提供高性能、可扩展且灵活的分布式服务解决方案。此次发布的 Dubbo 3.3,通过 Triple X 的全新升级,突破了以往局限,实现了对南北向与东西向流量的全面支持,并提升了对云原生架构的友好性。
156 10
|
7月前
|
Dubbo Java 应用服务中间件
阿里巴巴资深架构师深度解析微服务架构设计之SpringCloud+Dubbo
软件架构是一个包含各种组织的系统组织,这些组件包括Web服务器,应用服务器,数据库,存储,通讯层),它们彼此或和环境存在关系。系统架构的目标是解决利益相关者的关注点。