Spring Cloud Alibaba——Nacos实现服务治理

引言

本博客总结微服务开发中各个微服务调用的实现,并使用 Nacos 完成服务注册和发现。

文章中会涉及到 maven 的使用,以及 spring boot 的一些知识。开发工具采用 IDEA 2020.2。

设计一个电商订单和商品购买微服务,实现微服务的注册发现与调用。

一、模块设计

本案例采用电商网站作为展示,涉及到的三个微服务有:shop-user、shop-product、shop-order,还有一个公共依赖模块shop-common。他们的依赖、调用关系如下所示:

shop-user 是用户微服务,端口是807x

shop-product 是商品微服务,端口是808x

shop-order 是订单微服务,端口是809x

三个微服务之间可以通过HTTP请求相互调用业务逻辑。

二、创建Maven父工程

为了便于依赖的管理,和项目维护,在实际生产中,往往通过父工程来管理各个 maven 微服务模块,和maven 依赖模块。

(在这里我需要简单说明一下这个大的maven 工程下面,如何理解各个子模块的关系。案例中包含了三个微服务(shop-user/shop-product/shop-order),和一个公共依赖模块(shop-common),它们都会作为一个 maven 子模块存放到父工程目录下,但实际上,在实际部署的时候,三个微服务是分开部署的,因为三个微服务之间的关系,除了通过父工程来统一维护一些依赖版本之外,没有什么在代码层面的耦合关系。而公共依赖模块则在代码层面耦合到各个模块中,部署之后,也是你中有我的概念。)

首先 New ——> Project ——> Maven ,选择好JDK 版本后,直接Next,跳过 archetype 选项。

填写必要的项目名称和存储位置,maven坐标等信息,点击finish:

idea可以快速为我们创建并打开新项目,由于  Maven 父工程只做版本管理,不需要写任何代码,因此一般都会直接删除 src 目录:

紧接着,我们需要修改父工程 pom 文件。它主要需要负责两件事:1、指定父工程   2、依赖版本的锁定

<?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>org.morty</groupId><artifactId>shop</artifactId><version>1.0-SNAPSHOT</version><!-- 指定父工程--><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.5.RELEASE</version></parent><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><!-- 版本锁定--><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Greenwich.SR5</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.1.1.RELEASE</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>
</project>

下表展示了 Spring Cloud Alibaba & Spring Cloud & Spring Boot 兼容关系:

Spring Cloud VersionSpring Cloud Alibaba VersionSpring Boot Version
---------------------
Spring Cloud Hoxton2.2.x.RELEASE2.2.x.RELEASE
Spring Cloud Greenwich2.1.x.RELEASE2.1.x.RELEASE
Spring Cloud Finchley2.0.x.RELEASE2.0.x.RELEASE
Spring Cloud Edgware1.5.x.RELEASE1.5.x.RELEASE

三、创建基础依赖模块

new——>Module...

添加必要的依赖:

<?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"><parent><artifactId>shop</artifactId><groupId>org.morty</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>shop-common</artifactId><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.58</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency></dependencies>
</project>

创建domain实体类,User、Product、Order,这样,其他三个微服务可以依赖使用:

package com.morty.domain;import lombok.Data;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;@Entity(name = "shop_user")
@Data
public class User {@Id// 数据库自增@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer uid;private String username;private String password;private String telephone;
}
package com.morty.domain;import lombok.Data;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;@Data
@Entity(name = "shop_product")
public class Product {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer pid;private String pname;// 商品价格private Double pprice;// 库存private Integer stock;
}
package com.morty.domain;import lombok.Data;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;@Data
@Entity(name = "shop_order")
public class Order {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer oid;private Integer uid;private String username;private Integer pid;private String pname;private Double pprice;/** 购买数量*/private Integer number;
}

四、创建微服务模块

依次创建shop-user、shop-product、shop-order 三个微服务,并依赖 shop-common。篇幅有限,以 shop-product 为例。

1、和shop-common的创建方式一样,新建一个 Module,并命名 shop-product,修改pom文件,添加 shop-common依赖和 web starter:

    <dependencies><!-- 依赖基础模块--><dependency><groupId>org.morty</groupId><artifactId>shop-common</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies>

2、创建spring boot 启动类:

@SpringBootApplication
@Slf4j
public class ProductApplication {public static void main(String[] args) {SpringApplication.run(ProductApplication.class);log.info("-----------启动成功------------");}
}

3、修改配置文件:

server:port: 8081
spring:application:name: service-productdatasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/shop?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=trueusername: rootpassword: 123456jpa:properties:hibernate:hbm2ddl:auto: update# InnoDB方言dialect: org.hibernate.dialect.MySQL5InnoDBDialect

然后就是创建 controller、service、dao:

@Slf4j
@RestController
@RequestMapping("/product")
public class ProductController {@Autowiredprivate ProductService productService;/*** 查询订单信息* @param pid* @return*/@GetMapping("/{pid}")public Product getProduct(@PathVariable("pid") Integer pid) {log.info("收到查询商品信息请求,商品编号:{}", pid);Product product = productService.getProduct(pid);log.info("商品信息查询成功:{}", JSON.toJSONString(product));return product;}
}
@Service
public class ProductService {@Autowiredprivate ProductDao productDao;public Product getProduct(Integer productId) {return productDao.findById(productId).get();}
}
public interface ProductDao extends JpaRepository<Product, Integer> {
}

最后,手动创建 shop 数据库,然后启动服务,可以看到表已经自动创建好了,向 shop_product 表插入一条商品信息:

INSERT INTO `shop_product`(pname, pprice, stock) VALUES('皮大衣', '120', '20');

打开浏览器,访问接口,可以正常返回:

五、微服务调用

按照类似的步骤创建好了三个微服务之后,我们来实现订单到商品的微服务调用。需要说明的是,任何两个服务之间都是可以通过http请求进行调用,而不完全需要服务治理功能,也就是说,如果我们指定了ip和端口号,实际上就可以实现微服务的调用。

为了演示方便,这里只列出关键代码,并去掉了Service的接口层。

提供必要的 restTemplate:

    @Beanpublic RestTemplate restTemplate() {return new RestTemplate();}

DAO:

public interface OrderDao extends JpaRepository<Order, Integer> {
}

Service:

@Service
public class OrderService {@Autowiredprivate OrderDao orderDao;public void createOrder(Order order) {orderDao.save(order);}
}

Controller:

@Slf4j
@RestController
@RequestMapping("/order")
public class OrderController {@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate OrderService orderService;/**** 下单* @param pid* @return*/@GetMapping("/prod/{pid}")public Order order(@PathVariable("pid") Integer pid) {log.info("接收到{}号商品的下单请求,准备调用商品微服务", pid);// 调用商品微服务,查询商品信息Product prod = restTemplate.getForObject("http://localhost:8081/product/" + pid, Product.class);log.info("查询到{}号商品信息,内容是:{}", pid, JSON.toJSONString(prod));// 下单(即创建订单并保存)Order order = new Order();order.setUid(1);order.setUsername("测试用户");order.setPid(pid);order.setPname(prod.getPname());order.setPprice(prod.getPprice());order.setNumber(1);// 订单入库orderService.createOrder(order);log.info("创建订单成功,订单信息为:{}", JSON.toJSONString(order));return order;}
}

然后在配置文件中指定 8091 端口号,以及数据库地址等必要信息。启动 OrderApplication 和 ProductApplication,调用 /order/prod/{pid} 接口:

检查控制台打印的日志:

订单微服务:

商品微服务:

同时数据库也出现了刚才添加的订单记录:

六、服务治理

在前面的微服务调用案例中,我们通过 restTemplate 对象,配合 ip + port 的形式,实现了最简单的订单微服务到商品微服务的调用逻辑。

但这在实际生产中会存在较大的问题:

1、一旦服务提供者的地址发生变化,就不得不去修改服务调用者的代码,即便是使用配置文件也治标不治本。

2、在高并发场景中,服务一般需要进行集群部署,会有多个服务提供者实例。那么就需要通过负载均衡调用不同的服务提供者,来分散单个服务实例的访问压力,上面这种调用方式显然无法满足负载均衡的要求。

3、一旦微服务变得越来越多,如果管理服务清单将会是一个大问题。

基于以上几点,就有了服务治理的概念:

服务治理是微服务架构中最核心、最基本的模块。用于实现各个微服务的自动化注册和发现。

服务注册:在服务治理框架中,都会构建一个注册中心。每个服务单元向注册中心登记自己提供服务的详细信息。注册中心会基于这些微服务的详细信息,生成一张服务清单。注册中心需要以心跳的方式检测清单中服务是否可用,如果发现心跳异常的服务,会从服务清单中剔除不可用的服务。

服务发现:服务消费者向注册中心咨询服务,并获取所有服务的实例清单,实现对具体服务的访问。

常用的服务治理框架有:

ZooKeeper:是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要用来解决分布式应用中经常遇到的一些数据管理问题,如统一命名服务、状态同步服务、集群管理、分布式应用配置项管理等。

Eureka:是Spring Cloud Netfix 中的重要组件,主要作用是做服务注册和发现,但现在已经闭源。

Nacos:是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它是 Spring cloud Alibaba 的组件之一,负责服务注册发现和服务配置,可以认为是 Eureka + Config 的组合升级版服务治理框架。

七、Nacos-discovery 实现微服务调用

7.1 启动 Nacos 服务

首先,如果想使用 Nacos 注册中心服务,必须到官网上下载启动压缩包。值得一提的是,原来的 Eureka 是通过 Spring boot 构建一个专门用于实现注册发现的微服务,这需要我们手动去构建这样一个重要的架构组件,但是 Nacos 则提供了独立的启动程序,让开发者可以开箱即用,进一步提高了微服务部署的效率。

nacos 下载地址:https://nacos.io/zh-cn/docs/quick-start.html

不论你是在 Windows 环境上学习和练习,还是在 Linux 服务器上安装部署,都只需要简单的一键启动即可。

启动成功后,我们通过浏览器访问控制台,默认用户名和密码都是 nacos,下图登录成功后进入首页:

7.2 将微服务注册到 Nacos

以 shop-product 为例,演示如何将微服务注册到 Nacos。

1、微服务中添加 nacos 客户端依赖:

        <!-- nacos 客户端--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-nacos-discovery</artifactId></dependency>

2、为主类添加@EnableDiscoveryClient 注解:

@Slf4j
@EnableDiscoveryClient
@SpringBootApplication
public class ProductApplication {public static void main(String[] args) {SpringApplication.run(ProductApplication.class);log.info("-----------启动成功------------");}
}

3、配置 Nacos Server 地址:

spring:cloud:nacos:discovery:server-addr: localhost:8848

4、启动微服务,查看 Nacos 控制台:

7.3 Nacos 实现微服务调用

针对前面第五节订单到商品的微服务调用方式,我们调整一下restTemplate 代码,以服务治理推荐的方式来实现微服务调用。

引入服务发现客户端对象:

@Autowired
private DiscoveryClient discoveryClient;

修改代码:

启动 shop-order 、shop-product 微服务,它们会自动注册到 Nacos 中。

重新调用下单接口,可以看到接口依然调用成功:

总结

微服务注册中心的主要功能是负责服务注册发现,它会生成一张注册服务清单,可以简单理解为一个服务名称和对应服务地址的对照表,服务消费者使用服务名称调用服务提供者的接口时,会直接发送到对应地址:

微服务如果想要注册到 Nacos Server,需要完成三件事:

1、添加 nacos-discovery 依赖

2、启动服务发现客户端,即添加 @EnableDiscoveryClient 注解到主类

3、配置 Nacos server 注册中心地址和端口号

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/558686.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Cloud —— Feign 实现服务调用

引言 本篇博客简单介绍 Feign 的基础知识和基本应用&#xff0c;以前一篇博客《Spring Cloud Alibaba——Nacos实现服务治理》为代码基础&#xff0c;实现更简单的微服务调用方式。 一、什么是Feign restTemplate 实现的微服务调用方式&#xff1a; // 调用商品微服务&…

Spring Cloud —— 负载均衡与 Ribbon 应用

引言 本篇博客简单介绍微服务负载均衡的概念&#xff0c;并通过 IDEA 多端口启动应用的方式&#xff0c;模拟多个应用实例&#xff0c;使用自定义和 Ribbon 两种方式实现基本的负载均衡策略。 微服务代码以《Spring Cloud Alibaba——Nacos实现服务治理》为基础。 一、什么是…

Spring —— 容器内部逻辑

引言 上一篇关于IoC容器的详解《Spring —— IoC 容器详解》真是工程浩大&#xff0c;可以说Spring官网对核心中的核心IOC容器做了非常全面的使用说明&#xff0c;包括在《Spring揭秘》中让我一直没有成功的Method Injection&#xff0c;官网也解决了我的疑惑&#xff0c;并最…

2020 年度总结

2020年给我的感觉是短平快的一年。 由于年初的新冠肺炎疫情&#xff0c;我大半年都呆在北京的破旧出租屋里写代码。整个春天和夏天&#xff0c;平平无奇。 2月1日返京&#xff0c;居家办公&#xff0c;夜跑。8月复工&#xff0c;疯狂爆痘、烂脸&#xff0c;月末落户天津。9月…

JDBC——概述与JDBC的使用

引言 一直希望深入学习一下数据库持久化技术&#xff0c;接触过Hibernate、Mybatis&#xff0c;也使用过Spring事务管理来控制回滚操作&#xff0c;但是越发觉得底层知识有一定的知识盲区和空洞。 很多ORM框架都是基于JDBC规范来进行构建的&#xff0c;因此&#xff0c;学习J…

JDBC——编程式事务的实现逻辑

引言 数据库事务的概念和基础&#xff0c;总结在《MySQL 基础 ————事务与隔离级别总结》。 本篇博客通过“JDBC 纯编码”方式实现事务控制&#xff0c;完成一个 A 给 B 转账的小功能&#xff0c;在进一步熟练JDBC的编程流程的同时&#xff0c;重点关注 Java 语言如何操作…

排序算法——插入排序

一、算法思想 给定一个无序数列&#xff0c;模拟一个指针从第0位开始向后&#xff0c;始终保持当前位置左边的数列是有序的。 指针位置上的元素依次与前面的元素比较&#xff0c;当遇到小于自己的数或右边已经没有元素时&#xff0c;都停止比较&#xff0c;开始下一轮。 生活…

Linux 底层原理 —— epoll 与多路复用

引言 epoll 是 Linux 系统下高性能网络服务的必备技术&#xff0c;很多面试中高频出现的 Nginx、Redis 都使用了这一技术&#xff0c;本文总结 linux 多路复用模型的演变过程&#xff0c;看一看epoll 是如何实现高性能的。 一、相关基础知识 1.1 文件描述符 文件描述符&…

异或运算的应用

一、基础知识 异或运算&#xff0c;相异为1。 异或运算是一种常用的位运算&#xff0c;在算法题中&#xff0c;对于避免额外的空间复杂度有独特的用处。 异或运算也被称为“无进位相加”&#xff0c;它具有以下特性&#xff1a; 特性1&#xff1a;0 ^ N N 特性2&#xff1a…

单向队列、双端队列、栈的模型实现

引言 自己实现简单的队列、栈的逻辑结构。 队列都包含头和尾两个指针&#xff0c;简单的单向队列只能在一端&#xff08;如&#xff1a;head端&#xff09;入列&#xff0c;在另一端&#xff08;如&#xff1a;tail 端&#xff09;出列&#xff1b;双端队列可以在 head 进出&…

递归算法及其时间复杂度分析

引言 “递归” 一词是比较专业的计算机术语&#xff0c;在现实生活中&#xff0c;有一个更可爱的词——“套娃”。如果把“递归算法”叫做“套娃算法”&#xff0c;或许可以减少一些恐惧程度。 套娃是有限的&#xff0c;同样&#xff0c;递归也是有限的&#xff0c;这和我们经…

算法设计中的基础常用代码

引言 本篇博客旨在记录一些基础算法知识的常见组合用法&#xff0c;以及何时使用&#xff0c;需要注意的问题等&#xff0c;长期更新。 为什么要这样总结呢&#xff1f;难道掌握了位运算、常用算法工具API的定义还不够吗&#xff1f; 这是因为某些知识比如 &、 |、 ~、 …

Redis —— 常用命令一览

引言 参考《菜鸟教程 Redis 常用命令》&#xff0c;其中红色为极其重要&#xff0c;蓝色为重要。 一、总览 二、key相关命令 三、String 相关命令 四、Hash 相关命令 五、List 相关命令 六、Set 相关命令 七、ZSet 相关命令

Redis 实用技术——消息发布和订阅

引言 发布订阅模型是redis的重要功能&#xff0c;它可以像网站动态一样&#xff0c;将消息发送到多个订阅者的主页里。 一、常用命令 二、消息格式 消息是一个有三个元素的多块响应&#xff1a; 如上图&#xff0c;发布者向 mysub 频道发送了一条消息&#xff0c;redis会返回…

Redis 实用技术——事务

引言 redis的事务不像关系型数据库的事务那样完整。 “快”是redis的特征&#xff0c;在事务管理的过程中&#xff0c;使用muti命令开启事务块&#xff0c;当输入多条命令后&#xff0c;再使用exec命令执行事务块中的全部命令。 Redis事务可以保证两件事&#xff1a; 1、隔…

排序算法——归并排序的相关问题

一、小和问题 问题描述&#xff0c;给定一个数组&#xff0c;如[1, 3, 2, 6, 5]&#xff0c;计算每个数左边小于自己的所有数的和&#xff0c;并累加。例如&#xff1a; 1左边没有数 3左边有一个小于自己的数 1 2左边有一个小于自己的数 1 6左边有三个小于自己的数 1 3 2 6…

经典数据结构——堆的实现

一、完全二叉树 堆是一种完全二叉树&#xff0c;什么是完全二叉树&#xff1f; 简单的说&#xff0c;一棵满二叉树表示的是所有节点全部饱和&#xff0c;最后一层全部占满&#xff1a; 而完全二叉树指的是满二叉树的最后一层&#xff0c;所有叶子节点都从左往顺序排满&#x…

排序算法 —— 堆排序

引言 此文基于《经典数据结构——堆的实现》中堆结构&#xff0c;实现一个以堆处理排序的算法。 一、算法思想 基于堆结构的堆排序的算法思想非常简单&#xff0c;循环获取大根堆中的最大值&#xff08;0位置的根节点&#xff09;放到堆的末尾&#xff0c;直到将堆拿空。 由…

经典数据结构——前缀树

引言 前缀树——trie /ˈtraɪ//树&#xff0c;也叫作“单词查找树”、“字典树”。 它属于多叉树结构&#xff0c;典型应用场景是统计、保存大量的字符串&#xff0c;经常被搜索引擎系统用于文本词频统计。它的优点是利用字符串的公共前缀来减少查找时间&#xff0c;最大限度…

排序算法 —— 计数排序

引言 计数排序是桶排序思想的一种具体实现&#xff0c;针对一些具有特殊限制的样本数据&#xff0c;如公司员工年龄&#xff0c;那么样本数据本身就一定在0~200之间&#xff0c;针对这样的数据&#xff0c;使用从0到200 的桶数组&#xff0c;桶的位置已经是有序的&#xff0c;…