读书笔记:大型分布式网站架构设计与实践(1)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiaoyi52/article/details/82705836

原计划花一年半时间看完十二本书,目录如下:

(1)大型分布式网站架构设计与实践(陈康贤,电子工业出版社)
(2)大型网站系统与Java中间件实践(曾宪杰,电子工业出版社)
(3)Java并发编程实战(童云兰等译,机械工业出版社)
(4)Java并发编程的艺术(方腾飞,机械工业出版社)
(5)深入理解Java虚拟机(机械工业出版社)
(6)Effective Java中文版(机械工业出版社)
(7)Spring源码深度解析(人民邮电出版社)
(8)MySQL5.6从零开始学(清华大学出版社)
(9)数据库索引设计与优化(电子工业出版社)
(10)Redis设计与实现(黄健宏,机械工业出版社)
(11)分布式服务框架原理与实践(李林锋,电子工业出版社)
(12)深入分析Java web技术内幕(许令波,电子工业出版社)

到目前为止已经看完了一半,剩下的估计看不完。而看过的书,回忆起来发现也忘记得差不多了。因此温习一遍并留下笔记,后面也需要经常回过头来看看,温故而知新。

1 面向服务的体系架构SOA

第一章节一共分为四小节,分别介绍了基于TCP协议的RPC,基于HTTP协议的RPC,服务的路由和负载均衡,以及HTTP服务网关。

1.1 基于TCP的RPC

1.1.1 什么是RPC

在SOA或者微服务架构中,有一个很基础的概念就是RPC,全称是Remote Process Call,即远程过程调用。RPC的实现方式较多,如RMI,WebService等。RPC是相对于本地调用来说的,它是指调用非本地的服务器上的方法。比如在微服务架构中,一个微服务系统调用另一个微服务系统提供的某个接口,这次调用过程就是一次RPC。RPC给系统的处理能力和吞吐量带来了近似于无限制提升的可能,这是系统发展到一定阶段必然性的变革,也是实现分布式计算的基础。

一次完整的RPC过程描述如下:服务调用方(也可叫消费者)发送RPC请求到服务提供方(生产者),服务提供方根据调用方提供的参数执行请求方法,将执行结果返回给调用方。

在一次RPC过程中,服务调用方发出的请求参数和服务提供方返回的结果需要通过网络进行传输,这就涉及到序列化和反序列化操作。而随着业务的发展,服务调用者和服务提供者往往需要进行扩容,以服务集群的形式存在。这样,一次调用请求分发给哪一台服务器进行处理,就涉及到服务路由与负载均衡。

1.1.2 序列化/反序列化

无论是何种类型的数据,最终都要转换成二进制流在网络上进行传输。数据的发送方需要将对象转换成二进制流,才能在网络上进行传输,而数据的接受方则需要把二进制流再恢复为对象。将对象转换成二进制流的过程称为对象的序列化,将二进制流恢复为对象的过程称为对象的反序列化。

序列化/反序列化框架中,常用的有Google的Protocal Buffers、Java本身内置的序列化方式、Hessian以及JSON和XML等。各种框架都有各自的使用场景和优缺点。

1.1.3 基于TCP协议实现RPC

基于Java的Socket API,可以实现一个简单的RPC调用。代码示例如下:

public class Provider {
    private static Map<String, Object> services = new HashMap<>();
    static {
        services.put("cn.tonghao.component.book1.firstChapter.ISayHelloService", new SayHelloServiceImpl());
    }

    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(8888);
        while (true) {
            Socket socket = serverSocket.accept(); // 建立socket监听
            ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); // 获得socket端口的输入流
            String interfaceName = input.readUTF(); // 接口名
            String methodName = input.readUTF(); // 方法名
            Class<?>[] parameterTypes = (Class<?>[]) input.readObject(); // 参数类型
            Object[] argus = (Object[]) input.readObject(); // 参数对象

            Class serviceInterfaceClass = Class.forName(interfaceName);
            Object service = services.get(interfaceName); // 取得服务实现的对象
            Method method = serviceInterfaceClass.getMethod(methodName, parameterTypes); // 获得调用方法
            Object result = method.invoke(service, argus);

            ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
            output.writeObject(result);
        }
    }
}

public class Consumer {

    public static void main(String[] args) throws Exception {
        String interfaceName = ISayHelloService.class.getName();
        Method method = ISayHelloService.class.getMethod("sayHello", String.class);
        Object[] argus = {"hello"};
        Socket socket = new Socket("127.0.0.1", 8888);

        ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
        output.writeUTF(interfaceName);
        output.writeUTF(method.getName());
        output.writeObject(method.getParameterTypes());
        output.writeObject(argus);

        ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
        Object result = input.readObject();
        System.out.println(result.toString());
    }
}
public interface ISayHelloService {
    String sayHello(String str);
}

public class SayHelloServiceImpl implements ISayHelloService {
    @Override
    public String sayHello(String str) {
        return str;
    }
}

先运行服务提供者provider,再运行消费者consumer,最终会输出hello。这个简单的例子展示了一次RPC调用。服务调用方将请求参数发送给服务提供方,服务提供方收到调用方的请求参数之后,通过反射机制获取到要执行的方法,最终得到返回的结果。

1.2 基于HTTP协议的RPC

由于TCP属于网络传输层协议,所以基于TCP协议实现的RPC能够灵活地对协议字段进行定制,减低网络开销,但同时要关注很多底层的复杂细节,实现的代价很高,难以跨平台调用。

而对于基本HTTP协议的实现来说,很多成熟的开源web容器已经帮其处理好了底层细节,开发人员只需要关注业务实现,而不用理会复杂的底层细节。当然,由于是上层协议,封装了很多底层细节的东西,在同等网络环境下, 使用HTTP协议传输相同的内容,效率会比基于TCP协议的数据传输要低。

基于HTTP协议的RPC一般使用JSON或XML作为数据传输的格式,RPC或Restful风格的URL链接风格,通过http协议进行远程过程调用。可以通过URLConnection或者HttpClient工具来发送http请求。

构建一次简单的基于HTTP的RPC是比较容易的:搭建一个最基本的springMVC框架,springMVC的controller可以提供restful风格的服务接口,启动之后该服务器就是一个服务提供者。然后使用URLConnnection或者HttpClient发送一个Http请求,调用服务提供者并得到返回结果,这样一次简单的RPC过程就完成了。

1.3 服务路由和负载均衡

在SOA架构中,一个服务消费者可能要消费多个服务,而不同的服务是由不同的服务提供者提供的,将不同的服务请求分发到给不同的服务提供者,就是所谓的服务路由。为了提高服务能力,一般服务提供者都是以集群的形式存在的。服务请求被路由给服务提供者(集群)之后,必须根据均衡算法和规则,选取其中一台服务器进行处理,这个过程就称为服务的负载均衡。

当服务规模较小时,可以采用硬编码的方式将服务地址和配置写在代码中,通过编码方式解决服务路由和均衡问题,也可以通过传统的硬件负载均衡设备如F5等,或者采用LVS或Nginx等软件解决方案。

当服务越来越多,规模越来越大时,硬编码方式是行不通的,而硬件负载均衡设备或者LVS/Nginx等软件方案也会存在单点故障问题,即一旦服务路由或负载均衡服务器宕机,依赖它的服务均将失效。

此时就需要一个能够动态注册和获取服务信息的地方,来统一管理服务名称和其对应的服务器列表信息,这就是所谓的服务配置中心。

服务注册中心应该提供以下能力:
服务提供者在启动时,将其提供的服务名称、服务器地址注册到服务配置中心

服务消费者通过服务配置中心来获得需要调用的服务的机器列表,通过负载均衡算法,选取其中一台服务器进行调用。

当服务器宕机或者下线时,该服务器地址能够动态地从服务配置中心里面移除,并通知相应的服务消费者(否则消费者会调用到已经失效的服务从而导致错误)。

服务消费者在第一次调用服务时需要查询服务配置中心,将查询到的信息缓存到本地。后续调用只需要从本地查询服务地址即可,除非服务的地址列表有变更

服务配置解决了之前负载均衡设备导致的单点故障问题,并且大大减轻了服务配置中心的压力。一般来说,常用的服务注册中心有Zookeeper,Eureka等。

1.3.1 负载均衡算法与动态配置规则

负载均衡算法的种类有很多,常见的有轮询法、随机法、源地址哈希法、加权轮询法、加权随机法、最小连接法等,应根据具体的使用场景选取对应的算法。

轮询和随机法是比较简单的负载均衡算法,用的也比较多。源地址哈希法能保证相同的客户端ip地址的请求始终被发送到同一台服务器上,根据此特性可以在服务消费者和服务提供者之间建立有状态的session会话。最小连接法比较灵活和智能,根据后端服务器当前的连接情况,动态地选取其中当前积压连接数最少的一台服务器来处理当前请求,尽可能提供后端服务器的利用效率。但实现起来较为复杂。

上面说的都是固定的策略,有时候还是无法满足千变万化的需求。当固定策略无法满足使用需求时,可以使用动态配置规则。动态配置规则利用的是Groovy脚本,由于Groovy脚本语言能够直接编译成Java字节码,运行在Java虚拟机上,且能够很好地跟Java进行交互,因此利用其动态特性可以实现负载均衡规则的动态配置。

1.4 HTTP服务网关

一般服务消费者的请求来自于公共网络,服务提供者位于企业私有网络。相对于企业内部的私有网络而言,公共网络的环境复杂多变,因此必须有一个强大的安全体系,来保障相关数据和接口的安全,而不能说所有来自于公共网络的请求都能调用服务提供者。

这个强大的安全体系就是网关。Http服务网关接收来自外部各种APP的Http请求,完成相应的权限与安全检查,当校验通过后,根据传入的服务名称,到服务配置中心找到相应的服务节点名称,并加载对应服务提供者的地址列表,通过负载均衡算法,选取服务器发起远程调用,将客户端参数传递到后端服务器。服务提供方处理请求并返回响应结果,HTTP 服务网关接收到响应后再将响应输出给客户端APP。

对于外部来说,所有的请求都依赖网关进行服务路由以及请求转发,gateway是整个网络的核心节点,一旦网关失效,所有依赖它的外部APP都将无法使用。而且,gateway把握着外部的请求入口,其流量是整个后端集群流量之和。因此在设计之初,就需要考虑到系统流量的监控和容量规划,以及gateway集群的可扩展性,以便在流量达到极限之前,能够快速方便地进行系统扩容。

总结

对这一章内容的总结如下:

面向服务的体系架构SOA也可以叫分布式应用架构,它是在垂直应用架构的基础上发展而来的,适应了越来越复杂的业务发展需求。

基于HTTP进行RPC时,服务的选择和机器的选择就涉及到了服务路由和负载均衡。负载均衡算法有随机、轮询、地址哈希、权重随机、权重轮询、最小连接法等。当服务规模较小时,可以通过硬编码或者负载均衡硬件或软件来实现服务路由和负载均衡,而当服务规模扩大时,就必须引入服务注册中心。常用的服务注册中心有zookeeper和eureka,提供了动态的服务注册和查找功能。

在成熟的分布式应用架构中,为了安全考虑,还需要一个网关,用于过滤非法请求。由于网关是所有请求的入口,流量是所有后端的流量之和,在设计之时就需要考虑流量监控和容量规划。

猜你喜欢

转载自blog.csdn.net/xiaoyi52/article/details/82705836