SpringCloud微服务详细笔记(一):微服务介绍--微服务拆分--RestTemplate远程调用--Nacos注册中心

server/2024/10/18 3:22:59/

目录

1.认识微服务

1.1单体架构

1.2微服务

1.3SpringCloud

2.微服务拆分

2.1服务拆分原则

2.1.1什么时候拆?

2.1.2怎么拆?

2.2微服务项目结构:

2.3服务拆分示例:

2.4远程调用

2.4.1RestTemplate

2.4.2远程调用示例

2.5总结

3.服务治理

3.1注册中心原理

3.2Nacos注册中心

3.3服务注册

3.3.1.添加依赖

3.3.2.配置Nacos

3.3.3.启动服务实例

3.4服务发现

3.4.1引入依赖

3.4.2.配置Nacos地址

3.4.3.发现并调用服务


1.认识微服务

1.1单体架构

将业务的所有功能集中在一个项目中开发,打成一个包部署。

当项目规模较小时,这种模式上手快,部署、运维也都很方便,因此早期很多小型项目都采用这种模式。


单体架构的优缺点:
优点:
架构简单
部署成本低

缺点:
团队协作成本高:你们团队数十个人同时协作开发同一个项目,由于所有模块都在一个项目中,不同模块的代码之间物理边界越来越模糊。最终要把功能合并到一个分支,你绝对会陷入到解决冲突的泥潭之中。
系统发布效率低:任何模块变更都需要发布整个系统,而系统发布过程中需要多个模块之间制约较多,需要对比各种文件,任何一处出现问题都会导致发布失败,往往一次发布需要数十分钟甚至数小时。
系统可用性差:单体架构各个功能模块是作为一个服务部署,相互之间会互相影响,一些热点功能会耗尽系统资源,导致其它服务低可用。

1.2微服务

微服务架构:首先是服务化,就是将单体架构中的功能模块从单体应用中拆分出来,独立部署为多个服务。同时要满足下面的一些特点:
-单一职责:一个微服务负责一部分业务功能,并且其核心数据不依赖于其它模块。
-团队自治:每个微服务都有自己独立的开发、测试、发布、运维人员,团队人员规模不超过10人
-服务自治:每个微服务都独立打包部署,访问自己独立的数据库。并且要做好服务隔离,避免对其它服务产生影响

于是,单体架构存在的问题就得到了解决。
- 团队协作成本高:
  - 由于服务拆分,每个服务代码量大大减少,参与开发的后台人员在1~3名,协作成本大大降低
- 系统发布效率低:
  - 每个服务都是独立部署,当有某个服务有代码变更时,只需要打包部署该服务即可
- 系统可用性差:
  - 每个服务独立部署,并且做好服务隔离,使用自己的服务器资源,不会影响到其它服务。

微服务架构解决了单体架构存在的问题,特别适合大型互联网项目的开发,因此被各大互联网公司普遍采用。大家以前可能听说过分布式架构,分布式就是服务拆分的过程,其实微服务架构正式分布式架构的一种最佳实践的方案。

1.3SpringCloud

SpringCloud是目前国内使用最广泛的微服务框架,其官网地址:https://spring.io/projects/spring-cloud。

SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的开箱即用体验。

其中常见的组件包括:

另外,SpringCloud底层是依赖于SpringBoot的,并且有版本的兼容关系,如下:

2.微服务拆分

2.1服务拆分原则

服务拆分要考虑的两个问题:什么时候拆?怎么拆?

2.1.1什么时候拆?

创业型项目:一般是先采用单体架构,随着用户规模扩大、业务复杂后再逐渐拆分为微服务架构。这样初期成本会比较低,可以快速试错。但是,这么做的问题就在于后期做服务拆分时,可能会遇到很多代码耦合带来的问题,拆分比较困难(前易后难)。
确定的大型项目:资金充足,目标明确,可以直接选择微服务架构,避免后续拆分的麻烦。(前难后易)

2.1.2怎么拆?

拆分项目要做到:
- 高内聚:
每个微服务的职责要尽量单一,包含的业务相互关联度高、完整度高。
- 低耦合:每个微服务的功能要相对独立,尽量减少对其它微服务的依赖,或者依赖接口的稳定性要强。

高内聚首先是单一职责,但不能说一个微服务就一个接口,而是要保证微服务内部业务的完整性为前提。目标是当我们要修改某个业务时,最好就只修改当前微服务,这样变更的成本更低。

一旦微服务做到了高内聚,那么服务之间的耦合度自然就降低了。

当然,微服务之间不可避免的会有或多或少的业务交互,比如下单时需要查询商品数据。这个时候我们不能在订单服务直接查询商品数据库,否则就导致了数据耦合。而应该由商品服务对应暴露接口,并且一定要保证微服务对外接口的稳定性(即:尽量保证接口外观不变)。虽然出现了服务间调用,但此时无论你如何在商品服务做内部修改,都不会影响到订单微服务,服务间的耦合度就降低了。

拆分方式一般有两种:
纵向拆分:按照业务模块来拆分。例如一个项目中,可能有用户管理功能、订单管理功能、购物车功能、商品管理功能、支付功能等。那么按照功能模块将他们拆分为一个个服务,就属于纵向拆分。这种拆分模式可以尽可能提高服务的内聚性。
横向拆分:抽取公共服务,提高复用性。例如用户登录是需要发送消息通知,记录风控数据,下单时也要发送短信,记录风控数据。因此消息发送、风控数据记录就是通用的业务功能,因此可以将他们分别抽取为公共服务:消息中心服务、风控管理服务。这样可以提高业务的复用性,避免重复开发。同时通用业务一般接口稳定性较强,也不会使服务之间过分耦合。

2.2微服务项目结构:

一般微服务项目有两种不同的工程结构
- 独立Project:每一个微服务都创建为一个独立的工程,甚至可以使用不同的开发语言来开发,项目完全解耦。
  - 优点:服务之间耦合度低
  - 缺点:每个项目都有自己的独立仓库,管理起来比较麻烦

- Maven聚合:整个项目为一个Project,然后每个微服务是其中的一个Module
  - 优点:项目代码集中,管理和运维方便(授课也方便)
  - 缺点:服务之间耦合,编译时间较长

2.3服务拆分示例:

下面这个是一个示例项目,项目结构如下。

我们的要求是拆分商品服务功能。
1、在hamll中新建Module功能

2、填写信息---模块名称---选择Maven项目---选择JDK版本---点击Create完成创建

3、引入依赖:可以直接复制hm-service的依赖,然后删除一些不需要的依赖就可以了。

<?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><parent><groupId>com.heima</groupId><artifactId>hmall</artifactId><version>1.0.0</version></parent><artifactId>item-services</artifactId><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><!--common--><dependency><groupId>com.heima</groupId><artifactId>hm-common</artifactId><version>1.0.0</version></dependency><!--web--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--数据库--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--mybatis--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.2</version></dependency><!--单元测试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency></dependencies><build><finalName>${project.artifactId}</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

4、编写包和启动类---新建com.hmall.item包---在包下新建启动类(可以直接复制hm-service的启动类,然后改启动类的名称就好了)

package com.hmall.item;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@MapperScan("com.hmall.item.mapper")
@SpringBootApplication
public class ItemApplication {public static void main(String[] args) {SpringApplication.run(ItemApplication.class, args);}
}

5、 创建以后会用到的各种包--controller--domain--mapper--service等

6、配置文件:可以直接从hm-service中拷贝。

7、然后更改application.yaml的信息,其他两个不用改。

server:port: 8081  #端口需要改,不然会和hm-service的冲突
spring:application:name: item-services    #微服务名称也需要改profiles:active: local#数据库连接需要改:微服务的数据库需要独立,不能访问其他微服务的数据库,所以我们需要在docker中重新创建一个独立的mysql,但这里我们就只是为了模拟,#所以就在当前数据库新建一个database。把hamll数据库换了就行,这里换成了hm-item。datasource:url: jdbc:mysql://${hm.db.host}:3307/hm-item?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghaidriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: ${hm.db.pw}
mybatis-plus:configuration:default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandlerglobal-config:db-config:update-strategy: not_nullid-type: auto
logging:level:com.hmall: debugpattern:dateformat: HH:mm:ss:SSSfile:path: "logs/${spring.application.name}"
knife4j:enable: trueopenapi:title: 黑马商城商品管理接口文档     #修改文档名称为模块对应的功能。description: "黑马商城商品管理接口文档"email: zhanghuyi@itcast.cnconcat: 虎哥url: https://www.itcast.cnversion: v1.0.0group:default:group-name: defaultapi-rule: packageapi-rule-resources:- com.hmall.item.controller #改为item-services下的包

 8、从hm-service拷贝与商品管理有关的代码到item-service
导入顺序:domain---mapper---service---controller。因为是依赖关系。
 因为引入的import需要更改,可以设置自动导入import,避免一直需要按Alt+Enter
File->Settings->Editor->General->Auto Import->勾选右边两个有关import的选项


9、这里有一个地方的代码需要改动,就是ItemServiceImpl中的deductStock方法:

这也是因为ItemMapper的所在包发生了变化,因此这里代码必须修改包路径。

最后,还要导入数据库表。默认的数据库连接的是虚拟机,在你docker数据库执行课前资料提供的SQL文件:

最终,会在数据库创建一个名为hm-item的database,将来的每一个微服务都会有自己的一个database:

11、配置完所有东西后,启动item-services,然后访问商品微服务的swagger接口文档:
http://localhost:8081/doc.html

然后测试其中的根据id批量查询商品这个接口:

测试参数:100002672302,100002624500,100002533430,结果如下:

说明商品微服务抽取成功了。

2.4远程调用

在业务拆分的过程中,有时候我们会遇到比如这样的问题:购物车业务需要查询商品信息,但是商品信息查询的功能已经拆分开了,两者的数据库又不是同一个,导致我们无法查询商品信息。

虽然代码不是连通的,但是网络是连通的。

要想解决这样的问题,我们要使用微服务的远程调用的方法。

我们前端向服务端查询数据,其实就是从浏览器远程查询服务端数据。比如通过Swagger测试商品查询接口,就是向http://localhost:8081/items这个接口发起的请求:
而这种查询就是通过http请求的方式来完成的,不仅仅可以实现远程查询,还可以实现新增、删除等各种远程请求。

于是我们也可以通过Java代码发送http请求来实现远程调用

2.4.1RestTemplate

Spring给我们提供了一个RestTemplate工具,可以方便的实现Http请求的发送。使用步骤如下;
(1)注入RestTemplate到Spring容器

(2)发起远程调用

RestTemplate给我们提供了很多的方法,方便发送Http请求

2.4.2远程调用示例

先将RestTemplate注册为一个Bean:

注入RestTemplate

修改相应代码,发送http请求到另一个模块

然后开始接口调试----记住两个业务功能都要启动,item-services和cart-service都要启动。

查询成功

2.5总结

什么时候需要拆分微服务
- 如果是创业型公司,最好先用单体架构快速迭代开发,验证市场运作模型,快速试错。当业务跑通以后,随着业务规模扩大、人员规模增加,再考虑拆分微服务
- 如果是大型企业,有充足的资源,可以在项目开始之初就搭建微服务架构。

如何拆分?
- 首先要做到高内聚、低耦合
- 从拆分方式来说,有横向拆分和纵向拆分两种。纵向就是按照业务功能模块,横向则是拆分通用性业务,提高复用性

服务拆分之后,不可避免的会出现跨微服务的业务,此时微服务之间就需要进行远程调用。微服务之间的远程调用被称为RPC,即远程过程调用。RPC的实现方式有很多,比如:
- 基于Http协议
- 基于Dubbo协议
我们课堂中使用的是Http方式,这种方式不关心服务提供者的具体技术实现,只要对外暴露Http接口即可,更符合微服务的需要。

Java发送http请求可以使用Spring提供的RestTemplate,使用的基本步骤如下:
- 注册RestTemplate到Spring容器
- 调用RestTemplate的API发送请求,常见方法有:
  - getForObject:发送Get请求并返回指定类型对象
  - PostForObject:发送Post请求并返回指定类型对象
  - put:发送PUT请求
  - delete:发送Delete请求
  - exchange:发送任意类型请求,返回ResponseEntity

3.服务治理

前面我们实现了微服务拆分,并且通过Http请求实现了跨微服务的远程调用。不过这种手动发送Http请求的方式存在一些问题。

试想一下,假如商品微服务被调用较多,为了应对更高的并发,我们进行了多实例部署,如图:

此时,每个item-service的实例其IP或端口不同,问题来了:
item-service这么多实例,cart-service如何知道每一个实例的地址?
http请求要写url地址,cart-service服务到底该调用哪个实例呢?
如果在运行过程中,某一个item-service实例宕机,cart-service依然在调用该怎么办?
如果并发太高,item-service临时多部署了N台实例,cart-service如何知道新实例的地址?

为了解决上述问题,就必须引入注册中心的概念了,接下来我们就一起来分析下注册中心的原理。

3.1注册中心原理

微服务远程调用的过程中,包括两个角色:

  • 服务提供者:提供接口供其它微服务访问,比如item-service

  • 服务消费者:调用其它微服务提供的接口,比如cart-service

在大型微服务项目中,服务提供者的数量会非常多,为了管理这些服务就引入了注册中心的概念。注册中心、服务提供者、服务消费者三者间关系如下:

流程如下:

  • 服务启动时就会注册自己的服务信息(服务名、IP、端口)到注册中心

  • 调用者可以从注册中心订阅想要的服务,获取服务对应的实例列表(1个服务可能多实例部署)

  • 调用者自己对实例列表负载均衡,挑选一个实例

  • 调用者向该实例发起远程调用

当服务提供者的实例宕机或者启动新实例时,调用者如何得知呢?

  • 服务提供者会定期向注册中心发送请求,报告自己的健康状态(心跳请求)

  • 当注册中心长时间收不到提供者的心跳时,会认为该实例宕机,将其从服务的实例列表中剔除

  • 当服务有新实例启动时,会发送注册服务请求,其信息会被记录在注册中心的服务实例列表

  • 当注册中心服务列表变更时,会主动通知微服务,更新本地服务列表

3.2Nacos注册中心

目前开源的注册框架有很多,我们用Nacos来实现。
我们基于Docker来部署Nacos的注册中心,首先我们要准备MySQL数据库表,用来存储Nacos的数据。由于是Docker部署,所以大家需要将资料中的SQL文件导入到你Docker中的MySQL容器中:

最终表结构如下:

然后,找到课前资料下的nacos文件夹:

其中的nacos/custom.env文件中,有一个MYSQL_SERVICE_HOST也就是mysql地址,需要修改为你自己的虚拟机IP地址,还有端口,密码等:

然后,将课前资料中的nacos目录上传至虚拟机的/root目录。
进入root目录,然后执行下面的docker命令:

docker run -d \
--name nacos \
--env-file ./nacos/custom.env \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--restart=always \
nacos/nacos-server:v2.1.0-slim

容器部署完成后可以通过下面命令来查看Nacos的运行日志

docker logs -f nacos

启动完成后,访问下面地址:http://192.168.52.135:8848/nacos,注意将192.168.52.135替换为你自己的虚拟机IP地址。

首次访问会跳转到登录页,账号密码都是nacos

3.3服务注册

比如将item-service注册到Nacos中,有以下步骤:

3.3.1.添加依赖

item-servicepom.xml中添加依赖:

<!--nacos 服务注册发现-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

3.3.2.配置Nacos

item-serviceapplication.yml中添加nacos地址配置:

spring:application:name: item-service # 服务名称cloud:nacos:server-addr: 192.168.52.135:8848 # nacos地址

3.3.3.启动服务实例

为了测试一个服务多个实例的情况,我们再配置一个item-service的部署实例:

右击Modify options

添加VM options

设置不同的启动类名称和端口号。然后点击Apply--Ok

启动Item-services的多个启动类。

访问nacos控制台,可以发现服务注册成功:

点击详情,可以查看到item-service服务的两个实例信息:

3.4服务发现

服务的消费者要去nacos订阅服务,这个过程就是服务发现,步骤如下:

  • 引入依赖

  • 配置Nacos地址

  • 发现并调用服务

3.4.1引入依赖

服务发现除了要引入nacos依赖以外,由于还需要负载均衡,因此要引入SpringCloud提供的LoadBalancer依赖。
我们在cart-service中的pom.xml中添加下面的依赖:

<!--nacos 服务注册发现-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

可以发现,这里Nacos的依赖于服务注册时一致,这个依赖中同时包含了服务注册和发现的功能。因为任何一个微服务都可以调用别人,也可以被别人调用,即可以是调用者,也可以是提供者。
因此,等一会儿cart-service启动,同样会注册到Nacos。

3.4.2.配置Nacos地址

cart-serviceapplication.yml中添加nacos地址配置:

spring:cloud:nacos:server-addr: 192.168.150.101:8848  #nacos地址

3.4.3.发现并调用服务

服务调用者cart-service就可以去订阅item-service服务了。不过item-service有多个实例,而真正发起调用时只需要知道一个实例的地址。

因此,服务调用者必须利用负载均衡的算法,从多个实例中挑选一个去访问。常见的负载均衡算法有:
服务发现需要用到一个工具,DiscoveryClient,SpringCloud已经帮我们自动装配,我们可以直接注入使用:

接下来,我们就可以对原来的远程调用做修改了,之前调用时我们需要写死服务提供者的IP和端口:

但现在不需要了,我们通过DiscoveryClient发现服务实例列表,然后通过负载均衡算法,选择一个实例去调用:


然后通过Swagger调试,成功。


http://www.ppmy.cn/server/116970.html

相关文章

[网络][CISCO]Cisco-PIX配置详解

Cisco PIX防火墙配置指南 任何企业安全策略的一个主要部分都是实现和维护防火墙&#xff0c;因此防火墙在网络安全的实现当中扮演着重要的角色。防火墙通常位于企业网络的边缘&#xff0c;使内部网络与Internet之间或与其他外部网络互相隔离&#xff0c;并限制网络互访&#x…

【C++ 高频面试题】new、delete 与 malloc、free的区别

文章目录 1. new 和 malloc 有什么区别2. delete 和 free 有什么区别&#xff1f;3. 堆和栈的区别 1. new 和 malloc 有什么区别 &#x1f427;类型安全问题&#xff1a; &#xff08;1&#xff09;new是C的运算符&#xff0c;可以为对象分配内存并调用相应的构造函数。 &#…

HSmartWindowControl 滚轮缩放 交互式绘制ROI 可修改 存储

一直想达到这个效果,奈何智商简单;今天来做一下记录; 个人习惯 Winform 新建以后删除xxx.designer.cs再双击Form1.cs设计器;就会在该 文件下 自动生成InitializeComponent() 且可 由设计器生成的所有winform界面程序则会出现在InitializeComponent()里面; 这样的好处很多,所…

HCIE和CCIE,哪个含金量更高点?

在现在内卷的大环境下&#xff0c;技术岗可谓人人自危&#xff0c;也因此各种认证的重视程度直线升高。 特别是华为认证的HCIE和思科认证的CCIE&#xff0c;它们都代表着网络技术领域的顶尖水平。 但面对这两个高含金量的认证&#xff0c;不得不让人问出这个问题&#xff1a;同…

第十七章 手动添加安全元素

文章目录 第十七章 手动添加安全元素添加安全标头元素标题元素的顺序 第十七章 手动添加安全元素 本主题主要介绍如何手动向 IRIS Web 服务和 IRIS Web 客户端发送的消息中添加安全元素。 以下主题提供了有关特定安全任务的详细信息。 添加安全标头元素 要将安全元素添加到…

01 Docker概念和部署

目录 1.1 Docker 概述 1.1.1 Docker 的优势 1.1.2 镜像 1.1.3 容器 1.1.4 仓库 1.2 安装 Docker 1.2.1 配置和安装依赖环境 1.3镜像操作 1.3.1 搜索镜像 1.3.2 获取镜像 1.3.3 查看镜像 1.3.4 给镜像重命名 1.3.5 存储&#xff0c;载入镜像和删除镜像 1.4 Doecker…

10分钟在网站上增加一个AI助手

只需 10 分钟&#xff0c;为您的网站添加一个 AI 助手&#xff0c;以便全天候&#xff08;7x24&#xff09;回应客户咨询&#xff0c;提升用户体验、增强业务竞争力。 方案概览 在网站中引入一个 AI 助手&#xff0c;只需 4 步&#xff1a; 创建大模型问答应用&#xff1a;我们…

工作流技术(WorkFlow)

什么是工作流 1.使用编程语言完成一套固定的审批流程 例如请假审批流程 订单配送流程 入职&#xff0c;辞职审批流程 2.使用场景 业务类&#xff1a;合同审批流程、订单处理流程、出入库审批流程等。 行政类&#xff1a;请假流程、出差流程、用车流程、办公用品申请流程…