你所不了解的微服务架构

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 一直以来,系统的架构设计是IT领域经久不衰的话题,也是构建每一个系统最核心且重要的部分之一。它决定了系统能否满足业务、技术、组织、灵活、可扩展性等多种要求,同时肩负起了解放程序员生产力的作用。 2016年底,由于业务的不断发展,我所在公司维护的项目也越来越“臃肿”。

一直以来,系统的架构设计是IT领域经久不衰的话题,也是构建每一个系统最核心且重要的部分之一。它决定了系统能否满足业务、技术、组织、灵活、可扩展性等多种要求,同时肩负起了解放程序员生产力的作用。

2016年底,由于业务的不断发展,我所在公司维护的项目也越来越“臃肿”。随着无数个版本的迭代,以及开发人员的不断增加,开发效率越来越低,每次投产的人力成本和时间成本都逐渐增加,我们一直在思索如何能破局。评估了各种方案后,最终微服务进入了我们的视野。

谈到微服务,大家众说纷纭,但却很难有一个清晰的概念来描述。微服务不是“银弹”,我理解的微服务是一种文化,而我们要做的就是将微服务的理念运用到实际开发中。那么下面我们就来聊聊微服务架构。


1.1 什么是微服务架构

1.2 垂直应用与微服务

1.3 实现一个最简单的微服务框架

1.4 主流微服务框架介绍

随着用户需求个性化、产品生命周期变短,微服务架构是未来软件架构朝着灵活性、扩展性、伸缩性以及高可用性发展的必然方向。这里主要将对比传统的垂直应用与分布式微服务应用之间的区别。

1.1 什么是微服务架构

微服务是一种软件架构风格,目标是将一个复杂的应用拆分成多个服务模块,每个模块专注单一业务功能对外提供服务,并可以独立编译及部署,同时各模块间互相通信彼此协作,组合为整体对外提供完整服务。

微服务架构就像是活字印刷术,每个文字模都可以看成是一个微服务,它可以独立地提供印刷服务,又可以将模块之间组合,最终形成一篇完整文章提供更为复杂的印刷服务。

由于每个模块都独立部署,各自拥有互不干扰的内存空间,模块之间无法直接调用,所以需要借助RPC(远程过程调用协议)或HTTP协议让各个模块之间传递通信报文及交换数据,实现远程调用,整个通信管理的过程也是微服务架构重要的组成部分。

1.2 垂直应用与微服务

MVC模式构建的垂直应用非常适合项目初期,使用其能够方便地进行开发、部署、测试,但随着业务的发展与访问量的增加,垂直应用的问题也随之暴露出来,而微服务架构可以很好地解决这些问题。

代码维护

垂直应用里,大部分逻辑都部署在一个集中化、单一的环境或服务器中运行。垂直应用程序通常很大,由一个大型团队或多个团队维护。庞大的代码库可能给希望熟悉代码的开发人员增加学习成本,还会让应用程序开发过程中使用的开发环境工具和运行容器不堪重负,最终导致开发效率降低,可能会阻止对执行更改的尝试。

微服务架构将这个庞大并且复杂的应用拆分成多个逻辑简单且独立的小应用,每个小应用交由不同的团队或开发人员维护,彼此之间互不干扰,通过标准接口互相通信。对于希望熟悉代码的开发人员来说只需掌握他所负责的应用即可,这样做的好处是简单、快速、逻辑清晰。

部署

垂直应用需要处理一个庞大的应用程序,编译、部署需要花费很长时间,一个小的修改就可能导致重新构建整个项目。

微服务架构中对其中某一个服务进行修改,只需重新编译、部署被改动的服务模块。

资源控制

垂直应用里,当请求量过大导致单台服务器无法支撑时,一般会将垂直应用部署在多台服务器形成服务集群,并通过反向代理实现负载均衡。集群中的每个服务必须部署完整的应用,但在实际业务需求中仅有部分功能使用频繁,但这种架构必须为不常用的功能分配计算资源。

微服务将提供功能的各服务拆分为多个服务模块,它具有天生的集群属性,能够轻松地根据用量部署。

例如系统中的消息功能使用频率占了整个系统的90%,而密码找回功能则只占到2%。为了分解消息功能的压力,以传统负载均衡的方式进行集群化时,每个服务必须为使用量只有2%的密码找回功能分配资源,这无疑造成了浪费。

在微服务架构中,消息功能使用率占据90%,则将消息模块多部署几个实例形成集群,而密码找回功能所在的用户模块只部署一个就可以了。

稳定

垂直应用中如果有一个小的问题,就可能使整个系统崩溃。

微服务所拆分出的各个模块中,由于模块之间的耦合度很低,当发生问题时影响范围被固定在该模块本身,整个系统依然健全。

1.3 实现一个最简单的微服务框架

e3ad41b0971a279ddb585bea42930a62b151d82c

基本工作流程如下。

① 客户端发起调用请求。

② 将调用的内容序列化后通过网络发给服务端。

③ 服务端接收到调用请求,执行具体服务并获得结果。

④ 将结果序列化后通过网络返回给客户端。

1.3.1 公共接口

在发起远程调用时,需要基于接口(Interface)来约定客户端与服务端所调用服务的具体内容。为了方便管理依赖关系,这里使用Maven构建应用并编写一些接口,以提供给客户端与服务端使用。

当然也可以使用普通的Java应用来实现此简单微服务框架,只需将该应用编译后的jar包提供给后续的服务端与客户端即可。

Maven 参数

1groupIdorg.book 
2artifactIdrpc-interface
3version:0.0.1-SNAPSHOT
4packagingjar

编写接口。

1public interface HelloService { 
2    public String hello(String name);
3}

1.3.2 服务端

新建用于提供服务的Maven应用,并引入刚编写的接口应用依赖。

Maven 参数

1groupIdorg.book 
2artifactIdrpc-server
3version:0.0.1-SNAPSHOT
4packagingjar

① 在pom.xml文件中引入依赖。

1<dependency> 
2    <groupId>org.book</groupId>
3    <artifactId>rpc-interface</artifactId>
4    <version>0.0.1-SNAPSHOT</version>
5</dependency>

② 实现服务接口。

1public class HelloServiceImple implements HelloService { 
2    public String hello(String name) {
3        System.out.println("收到消息:" + name);
4        return "你好:" + name;
5    }
6}

③ 编写监听服务类。

 1public class Server { 
2    private static ExecutorService executor = Executors.newFixedThreadPool(10);
3    private static final HashMap<String, Class> serviceRegistry = new HashMap<String, Class>();
4    public void register(Class serviceInterface, Class impl) {
5        //注册服务
6        serviceRegistry.put(serviceInterface.getName(), impl);
7    }
8    public void start(int port) throws IOException {
9        final ServerSocket server = new ServerSocket();
10        server.bind(new InetSocketAddress(port));
11        System.out.println("服务已启动");
12        while (true) {
13            executor.execute(new Runnable() {
14                public void run() {
15                    Socket socket = null;
16                    ObjectInputStream input = null;
17                    ObjectOutputStream output = null;
18                    try {
19                        socket = server.accept();
20                        // 接收到服务调用请求,将码流反序列化定位具体服务
21                        input = new ObjectInputStream(socket.getInputStream());
22                        String serviceName = input.readUTF();
23                        String methodName = input.readUTF();
24                        Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
25                        Object[] arguments = (Object[]) input.readObject();
26                        // 在服务注册表中根据调用的服务获取到具体的实现类
27                        Class serviceClass = serviceRegistry.get(serviceName);
28                        if (serviceClass == null) {
29                            throw new ClassNotFoundException(serviceName + " 未找到");
30                        }
31                        Method method = serviceClass.getMethod(methodName, parameterTypes);
32                        // 调用获取结果
33                        Object result = method.invoke(serviceClass.newInstance(), arguments);
34                        // 将结果序列化后发送回客户端
35                        output = new ObjectOutputStream(socket.getOutputStream());
36                        output.writeObject(result);
37                    } catch (Exception e) {
38                        e.printStackTrace();
39                    } finally {
40                        // 关闭资源
41                        try {
42                            if (socket != null) socket.close();
43                            if (input == null) input.close();
44                            if (output == null) output.close();
45                        } catch (IOException e) {
46                            e.printStackTrace();
47                        }
48                    }
49                }
50            });
51        }
52    }
53}
register()

提供一个数组保存所注册的服务接口及实现类。

start()

启动一个阻塞式的Socket服务用于等待客户端发起的调用请求,当收到请求后将码流反序列化成对象,并根据接口从注册列表中寻找具体实现类,最终通过反射的方式调用该实现类返回结果。

④ 注册服务并启动服务端。

1public class App { 
2    public static void main(String[] args) throws IOException {
3        Server server = new Server();
4        // 注册服务
5        server.register(HelloService.class, HelloServiceImple.class);
6        // 启动并绑定端口
7        server.start(8020);
8    }
9}

1.3.3 客户端

新建用于调用服务的Maven应用,并引入刚编写的接口应用依赖。

Maven 参数

1groupIdorg.book 
2artifactIdrpc-client
3version:0.0.1-SNAPSHOT
4packagingjar

① 在pom.xml文件中引入依赖。

1<dependency> 
2    <groupId>org.book</groupId>
3    <artifactId>rpc-interface</artifactId>
4    <version>0.0.1-SNAPSHOT</version>
5</dependency>

② 编写远程调用类。

 1public class Client<T> { 
2    @SuppressWarnings("unchecked")
3    public static <T> T get(final Class<?> serviceInterface, final InetSocketAddress addr) {
4        T instance = (T) Proxy.newProxyInstance(serviceInterface. getClassLoader(), new Class<?>[]{serviceInterface},
5                new InvocationHandler() {
6                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
7                        Socket socket = null;
8                        ObjectOutputStream output = null;
9                        ObjectInputStream input = null;
10                        try {
11                            // 连接服务端
12                            socket = new Socket();
13                            socket.connect(addr);
14                            // 将调用的接口类、方法名、参数列表等序列后发送给服务提供者
15                            output = new ObjectOutputStream(socket.getOutputStream());
16                            output.writeUTF(serviceInterface.getName());
17                            output.writeUTF(method.getName());
18                            output.writeObject(method.getParameterTypes());
19                            output.writeObject(args);
20                            // 同步阻塞等待服务器返回应答,获取应答后返回
21                            input = new ObjectInputStream(socket.getInputStream());
22                            return input.readObject();
23                        } finally {
24                            if (socket != null) socket.close();
25                            if (output != null) output.close();
26                            if (input != null) input.close();
27                        }
28                    }
29                });
30        return instance;
31    }
32}

使用JDK动态代理方式,根据提供的服务接口类将接口序列化成码流,向目标服务端发起Socket远程调用请求,获得服务端反馈的结果并反序列化成对象后返回。

③ 调用测试。

1public class App { 
2    public static void main(String[] args) throws IOException {
3        HelloService service = Client.get(HelloService.class, new InetSocketAddress ("localhost", 8020));
4        System.out.println(service.hello("RPC"));
5    }
6}

运行结果如下所示:

1// 客户端 
2hello : RPC
3// 服务端
4服务已启动
5收到消息:RPC

本章示例代码详见异步社区网站本书页面。

1.3.4 完善框架

服务之间的调用已基本实现,但想将它投入正式开发使用还有很多细节需要完善。

通信

当请求过大后会发现,BIO(同步阻塞式)的通信方式会消耗过多的资源导致服务器变慢甚至崩溃。

序列化与反序列化

在发起网络请求前,将对象转换成二进制串便于网络传输;收到消息请求后,将二进制串反转换成对象便于后续处理。序列化及反序列化直接影响到整个RPC框架的效率及稳定性。

服务注册中心

发起服务调用时,都需要指定服务提供方的访问地址(ip + 端口),如果当前服务提供方有多个或一个服务部署在多个机器上,调用时每次手动指定访问地址非常麻烦,这时就需要一个公共的注册中心去管理这些服务。

负载均衡

实施微服务的目的是为了让系统在进行横向扩展时能够拥有更多的计算资源,如果发现某一提供服务的机器负载较大,这就需要将新的需求转发到其他空闲的机器上。

服务监控

服务提供方有可能崩溃无法继续提供服务,在客户端进行调用时就需要将这些无法使用的服务排除掉。

异常处理

当服务端有异常发生导致无法返回正确的结果时,客户端并不知道该如何处理,只能等待并最终以超时结束此次远程调用请求。

以上所有的问题在后续将要介绍的Dubbo与Spring Cloud分布式框架中都得到了很好的解决,甚至基于Spring Boot构建的应用能让整个开发过程变得轻松愉快。

1.4 主流微服务框架介绍

1.4.1 Dubbo

阿里巴巴在2011年开源了Dubbo框架,虽然在2013年停止更新,但在2017年9月又重启维护并发布了新版本。目前已有很多的公司将自己的业务建立在Dubbo之上,同时阿里云也推出了企业级分布式应用服务EDAS,为Dubbo提供应用托管。

Dubbo采用Zookeeper作为注册中心,RPC作为服务调用方式,致力于提供高性能和透明化的RPC远程服务调用方案。它与Spring无缝集成,基于服务提供方(服务端)与服务调用方(客户端)角色构建简单模型,其优点是使用方便、学习成本低。

9b4d05463e33a4b10d66e08dccbd9155db55bb46


① 服务提供方发布服务到服务注册中心。

② 服务消费方从服务注册中心订阅服务。

③ 注册中心通知消息调用方服务已注册。

④ 服务消费方调用已经注册的可用服务。

⑤ 监控计数。

1.4.2 Spring Cloud

Spring Cloud基于Spring Boot实现,使用HTTP的RESTful风格API作为调用方式。它所包含的多个子项目共同构建了微服务架构体系。

f805613b0cbce65d6186420fa01770290f4fad48

Netflix Eureka

Spring Cloud 的服务注册中心提供服务注册、服务发现、负载均衡等功能。

Netflix Hystrix

当某个服务发生故障之后,则触发熔断机制(Hystrix)向服务调用方返回结果标识错误,而不是一直等待服务提供方返回结果,这样就不会使得线程因调用故障服务而被长时间占用不释放,避免了故障在分布式系统中的蔓延。

Netflix Zuul

代理各模块提供的服务,统一暴露给第三方应用。提供动态路由、监控、弹性、全等的边缘服务。

Config Server

分布式架构下多微服务会产生非常多的配置文件,分布式配置中心(Config Server)将所有配置文件交由GIT或SVN进行统一管理,避免出错。

Spring Boot

在使用Spring开发时,通常需要完成Spring框架及其他第三方工具配置文件的编写,非常麻烦。Spring Boot通过牺牲项目的自由度来减少配置的复杂度,约定一套规则,把这些框架都自动配置集成好,从而达到“开箱即用”。


本文摘自《微服务分布式构架开发实战

640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy


《微服务分布式架构开发实战

 龚鹏rico 著

 

点击封面购买纸书




本书并没有过多的探讨理论性的东西,基于现有成熟框架,围绕实际项目中遇见的具体需求,以微服务分布式架构的角度去逐一分解并且实现这些需求。掌握这些知识的读者,完全有能力快速搭建出可靠、高效、灵活的微服务分布式架构。

微服务好书重磅推荐:

640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy


深入理解Spring Cloud与微服务构建

方志朋 著

 

点击封面购买纸书




本书主要针对Java开发者构建微服务框架,作者比较青睐于Java 语言的Spring Cloud微服务框架,究其原因是Spring Cloud有快速开发、持续交付和易于部署等特点,且开源社区比较活跃,同时有国际巨头公司的推动。本书在Spring Cloud框架范围内,介绍了服务注册和发现的Eureka组件、负载均衡Ribbon组件、熔断器Hystrix组件、路由网关Zuul组件、Spring Cloud配置中心、服务链路追踪等内容,同时也与其他微服务框架做了对比,拓展了微服务知识的深度和广度。本书结构清晰,行文优美,每一个例子都经过作者斟酌再三,力求使用最简单的例子,将复杂的逻辑原理阐述清楚,让读者印象深刻。

延伸推荐

cbb0163f752cad51dacb3f9f10a7e4dd3439041f

今日话题

你看过最有意思的科技类公众号是什么?为什么?截止时间2月26日17时,小编将选出一名读者赠送异步图书一本。


点击关键词阅读更多新书:

Python|机器学习|Kotlin|Java|移动开发|机器人|有奖活动|Web前端|书单

095d751c037a00e8c6698aee1f67bd6521b63208


异步图书”后台回复“关注”,即可免费获得2000门在线视频课程;推荐朋友关注根据提示获取赠书链接,免费得异步图书一本。赶紧来参加哦!

点击阅读原文,查看本书更多信息

扫一扫上方二维码,回复“关注”参与活动!

640?tp=webp&wxfrom=5&wx_lazy=1




640?tp=webp&wxfrom=5&wx_lazy=1

640?tp=webp&wxfrom=5&wx_lazy=1


640?tp=webp&wxfrom=5&wx_lazy=1



640?tp=webp&wxfrom=5&wx_lazy=1



640?tp=webp&wxfrom=5&wx_lazy=1


640?tp=webp&wxfrom=5&wx_lazy=1


640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy



点击下方阅读原文,查看更多内容


相关实践学习
部署高可用架构
本场景主要介绍如何使用云服务器ECS、负载均衡SLB、云数据库RDS和数据传输服务产品来部署多可用区高可用架构。
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
1天前
|
设计模式 Kubernetes 数据库
构建高效可靠的微服务架构:后端开发的新范式
【5月更文挑战第7天】在现代软件开发的浪潮中,微服务架构已经成为一种流行的设计模式。它通过将应用程序分解为一组小的、独立的服务来提高系统的可维护性和扩展性。本文深入探讨了微服务架构的核心概念、优势以及如何利用最新的后端技术构建一个高效且可靠的微服务体系。我们将讨论关键的设计原则,包括服务的独立性、通信机制、数据一致性和容错性,并展示如何在云环境中部署和管理这些服务。
|
1天前
|
监控 负载均衡 数据安全/隐私保护
探索微服务架构下的服务网格(Service Mesh)实践
【5月更文挑战第6天】 在现代软件工程的复杂多变的开发环境中,微服务架构已成为构建、部署和扩展应用的一种流行方式。随着微服务架构的普及,服务网格(Service Mesh)作为一种新兴技术范式,旨在提供一种透明且高效的方式来管理微服务间的通讯。本文将深入探讨服务网格的核心概念、它在微服务架构中的作用以及如何在实际项目中落地实施服务网格。通过剖析服务网格的关键组件及其与现有系统的协同工作方式,我们揭示了服务网格提高系统可观察性、安全性和可操作性的内在机制。此外,文章还将分享一些实践中的挑战和应对策略,为开发者和企业决策者提供实用的参考。
|
1天前
|
缓存 监控 数据库
构建高性能微服务架构:后端开发的终极指南
【5月更文挑战第6天】 在现代软件开发的浪潮中,微服务架构以其灵活性、可扩展性和容错性引领着技术潮流。本文深入探索了构建高性能微服务架构的关键要素,从服务划分原则到通信机制,再到持续集成和部署策略。我们将透过实战案例,揭示如何优化数据库设计、缓存策略及服务监控,以确保系统的稳定性和高效运行。文中不仅分享了最佳实践,还讨论了常见的陷阱与解决之道,为后端开发者提供了一条清晰、可行的技术路径。
|
2天前
|
消息中间件 数据管理 持续交付
构建高效微服务架构的最佳实践
【5月更文挑战第6天】在动态和快速演变的现代软件开发领域,微服务架构已经成为促进敏捷开发和部署的关键模式。本文将深入探讨构建和维护高效微服务架构的策略,包括服务划分准则、通信机制、数据管理及持续集成与持续交付(CI/CD)的实施。通过分析不同业务场景下的应用案例,本文旨在为开发者提供一套行之有效的指导原则和实践方法,以支持他们构建可扩展、灵活且高效的微服务系统。
18 2
|
2天前
|
监控 负载均衡 API
微服务架构在现代企业中的应用与挑战
微服务架构已成为现代企业构建灵活且可扩展软件系统的首选。然而,随着其应用的普及,企业也面临着一系列新的挑战。本篇文章将探讨微服务架构的优势、实施时遇到的问题以及解决这些问题的策略。
|
2天前
|
Kubernetes Cloud Native 持续交付
构建高效云原生应用:Kubernetes与微服务架构的融合
【5月更文挑战第6天】 在数字化转型的浪潮中,企业正迅速采纳云原生技术以实现敏捷性、可扩展性和弹性。本文深入探讨了如何利用Kubernetes这一领先的容器编排平台,结合微服务架构,构建和维护高效、可伸缩的云原生应用。通过分析现代软件设计原则和最佳实践,我们提出了一个综合指南,旨在帮助开发者和系统架构师优化云资源配置,提高部署流程的自动化水平,并确保系统的高可用性。
22 1
|
2天前
|
API 持续交付 开发者
构建高效微服务架构:策略与实践
【5月更文挑战第6天】随着现代软件系统的复杂性增加,微服务架构逐渐成为企业开发的首选模式。本文深入分析了构建高效微服务架构的关键策略,并提供了一套实践指南,帮助开发者在保证系统可伸缩性、灵活性和稳定性的前提下,优化后端服务的性能和可维护性。通过具体案例分析,本文将展示如何利用容器化、服务网格、API网关等技术手段,实现微服务的高可用和敏捷部署。
|
2天前
|
监控 负载均衡 持续交付
构建高效微服务架构:后端开发的新趋势
【5月更文挑战第5天】在数字化转型的浪潮中,微服务架构以其灵活性、可扩展性和容错性成为企业追求的技术典范。本文深入探讨了微服务的核心组件、设计原则和实施策略,旨在为后端开发者提供构建和维护高效微服务系统的实用指南。通过分析微服务的最佳实践和常见陷阱,我们揭示了如何优化系统性能、保证服务的高可用性以及如何处理分布式系统中的复杂性。
|
3天前
|
缓存 NoSQL Java
构建高性能微服务架构:Java后端的实践之路
【5月更文挑战第5天】在当今快速迭代和高并发需求的软件开发领域,微服务架构因其灵活性、可扩展性而受到青睐。本文将深入探讨如何在Java后端环境中构建一个高性能的微服务系统,涵盖关键的设计原则、常用的框架选择以及性能优化技巧。我们将重点讨论如何通过合理的服务划分、高效的数据存储策略、智能的缓存机制以及有效的负载均衡技术来提升整体系统的响应速度和处理能力。
|
3天前
|
监控 持续交付 数据库
构建高效可靠的微服务架构:策略与实践
【5月更文挑战第5天】 在当今快速发展的软件开发领域,微服务架构已成为构建可扩展、灵活且容错的系统的首选模式。本文将探讨如何通过一系列经过验证的策略和最佳实践来构建一个高效且可靠的微服务系统。我们将深入分析微服务设计的核心原则,包括服务的细粒度划分、通信机制、数据一致性以及容错处理,并讨论如何利用现代技术栈来实现这些目标。文章将提供一套综合指南,旨在帮助开发者和架构师在保证系统性能的同时,确保系统的稳健性。
22 4