赞
踩
"微服务”一词源于 Martin Fowler的名为 Microservices的博文,可以在他的官方博客上找到 http://martinfowler.com/articles/microservices.html
微服务架构特点:
Spring Cloud 是分布式微服务架构的一站式解决方案,它提供了一套简单易用的编程模型,使我们能在 Spring Boot 的基础上轻松地实现微服务系统的构建。
Spring Cloud 被称为构建分布式微服务系统的“全家桶”,它并不是某一门技术,而是一系列微服务解决方案或框架的有序集合。它将市面上成熟的、经过验证的微服务框架整合起来,并通过 SpringBoot 的思想进行再封装,屏蔽调其中复杂的配置和实现原理,最终为开发人员提供了一套简单易懂、易部署和易维护的分布式系统开发工具包。
Spring Cloud 中包含了 spring-cloud-config、spring-cloud-bus 等近 20 个子项目,提供了服务治理、服务网关、智能路由、负载均衡、断路器、监控跟踪、分布式消息队列、配置管理等领域的解决方案。
Spring Cloud 本身并不是一个拿来即可用的框架,它是一套微服务规范,共有两代实现。
Spring Cloud Netflix 是 Spring Cloud 的第一代实现,主要由 Eureka、Ribbon、Feign、Hystrix 等组件组成。
Spring Cloud Alibaba 是 Spring Cloud 的第二代实现,主要由 Nacos、Sentinel、Seata 等组件组成。
SpringCloud抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式。==
严格来说,这两种方式各有优劣。虽然从一定程度上来说,后者牺牲了服务调用的性能,但也避免了上面提到的原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这在强调快速演化的微服务环境下,显得更加合适。都是一种SOA的实现。
首先,我们需要模拟一个服务调用的场景。方便后面学习微服务架构。在本案例中我们将采用最新稳定版本的SpringBoot和SpringColud。
SpringColud Version: *Hoxton.SR9*
Supported Boot Version: *2.3.7.RELEASE*
微服务中需要同时创建多个项目,先创建一个父工程,然后后续的工程都以这个工程为父,实现maven的聚合。这样可以在一个窗口看到所有工程。在实际开发中,每个微服务可独立一个工程。
<?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.studycloud</groupId> <artifactId>ClouadEasyBuy</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.7.RELEASE</version> <relativePath/> </parent> <properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF- 8</project.reporting.outputEncoding> <spring-boot.version>2.3.7.RELEASE</spring-boot.version> <spring-cloud.version>Hoxton.SR9</spring-cloud.version> </properties> <dependencies> <!--将来使用远程配置用 都继承吧--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <!--actuator 这是springboot程序的监控系统,可以实现健康检查,info信息等。 在使用之前需要引入spring-boot-starter-actuator,并做简单的配置即可--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
1)pom:
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.4</version>
</dependency>
</dependencies>
2)新建包easy.domain
3)复制粘贴entity实体类
4)父工程maven LifeCircle–install
5)目录结构:
1)pom:
<dependencies> <dependency> <groupId>com.studycloud</groupId> <artifactId>Common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId><scope>runtime</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.8</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3.4</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.3.7.RELEASE</version> <configuration> <mainClass>easy.EasyProductApp</mainClass> <jvmArguments>-Dfile.encoding=UTF-8</jvmArguments> </configuration> <executions> <execution> <id>repackage</id> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
2)application.yml:
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/easy_productdb?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8 username: root (填自己数据库的用户名) password: xxxx(填自己数据库的密码) application: name: easybuyproduct server: port: 7081 mybatis-plus: #mapper-locations: classpath:/mapper/.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl type-aliases-package: domain.mapper.productdb
3)目录结构
4)相关注解的使用
关于:@EnableDiscoveryClient 或 @EnableEurekaClient
在SpringCloud中当你需要使用Eureka注册中心的时候你在配置Eureka的客户端的时候需要在启动类上添加@EnableDiscoveryClient 或 @EnableEurekaClient注解 , SpringCloud Edgware 版本以上可以省略.
@SpringBootApplication @EnableTransactionManagement public class EasyProductApp { public static void main(String[] args) { SpringApplication.run(EasyProductApp.class,args); } } @RestController @RequestMapping("/productservice") public class ProductController { @Autowired private ProductServiceImpl productService; @GetMapping("/products") public List<TProduct> listProducts() { return productService.queryAllProducts(); } }
1)POM:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.studycloud</groupId> <artifactId>Common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>easy.EasyClientApplication</mainClass> <jvmArguments>-Dfile.encoding=UTF-8</jvmArguments> </configuration> </plugin> </plugins> </build>
2)application.yml:
spring:
application:
name: easybuyclient
thymeleaf:
cache: false
server:
port: 9081
3)目录结构
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) //不连数据库
public class EasyClientApplication {
public static void main(String[] args) {
SpringApplication.run(EasyClientApplication.class, args);
}
}
5)config代码:
@Configuration
public class ConfigBean {
@Bean
@LoadBalanced //没有LoadBalanced不能按服务名访问
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
6)controller代码:
@Controller public class ProductUiController { private static String productUrl = "http://localhost:7081"; @Autowired private RestTemplate restTemplate; @GetMapping("/products") public String products(Model model){ List<TProduct>ret=restTemplate.getForObject(productUrl+"/products",List.class); model.addAttribute("products",ret); return "productlist"; }
存在的问题:
1.在EasyClient中,我们把服务url地址硬编码到了代码中,不方便后期维护
2.EasyClient不清楚服务的状态,服务宕机也不知道
3.EasyProductPart只有1台服务,不具备高可用性
4.即便EasyProductPart形成集群,consumer还需自己实现负载均衡
这时候需用到Eureka
首先我们来解决第一问题,服务的管理
Eureka 一词来源于古希腊词汇,是“发现了”的意思。在软件领域,Eureka 是 Netflix 公司开发的一款开源的服务注册与发现组件。
Spring Cloud 将 Eureka 与 Netflix 中的其他开源服务组件(例如 Ribbon、Feign 以及 Hystrix 等)一起整合进 Spring Cloud Netflix 模块中,整合后的组件全称为 Spring Cloud Netflix Eureka。
Eureka 是 Spring Cloud Netflix 模块的子模块,它是 Spring Cloud 对 Netflix Eureka 的二次封装,主要负责 Spring Cloud 的服务注册与发现功能。
Spring Cloud 使用 Spring Boot 思想为 Eureka 增加了自动化配置,开发人员只需要引入相关依赖和注解,就能将 Spring Boot 构建的微服务轻松地与 Eureka 进行整合。
*Eureka 两大组件*
Eureka 采用 CS(Client/Server,客户端/服务器) 架构,它包括以下两大组件:
*Eureka Server*:Eureka 服务注册中心,主要用于提供服务注册功能。当微服务启动时,会将自己的服务注册到 Eureka Server。Eureka Server 维护了一个可用服务列表,存储了所有注册到Eureka Server 的可用服务的信息,这些可用服务可以在 Eureka Server 的管理界面中直观看到。
*Eureka Client*:Eureka 客户端,通常指的是微服务系统中各个微服务,主要用于和 EurekaServer 进行交互。在微服务应用启动后,Eureka Client 会向 Eureka Server 发送心跳(默认周期为 30 秒)。若 Eureka Server 在多个心跳周期内没有接收到某个 Eureka Client 的心跳,EurekaServer 将它从可用服务列表中移除(默认 90 秒)。
注:“心跳”指的是一段定时发送的自定义信息,让对方知道自己“存活”,以确保连接的有效性。大部分 CS 架构的应用程序都采用了心跳机制,服务端和客户端都可以发心跳。通常情况下是客户端向服务器端发送心跳包,服务端用于判断客户端是否在线。
*Eureka 服务注册与发现*
Eureka 实现服务注册与发现的原理,如下图所示。
上图中共涉及到以下 3 个角色:
*服务注册中心(Register Service)*:它是一个 Eureka Server,用于提供服务注册和发现功能。
*服务提供者(Provider Service)*:它是一个 Eureka Client,用于提供服务。它将自己提供的服务注册到服务注册中心,以供服务消费者发现。
*服务消费者(Consumer Service)*:它是一个 Eureka Client,用于消费服务。它可以从服务注册中心获取服务列表,调用所需的服务。
Eureka 实现服务注册与发现的流程如下:
搭建一个 Eureka Server 作为服务注册中心;
服务提供者 Eureka Client 启动时,会把当前服务器的信息以服务(spring.application.name)的方式注册到服务注册中心;
服务消费者 Eureka Client 启动时,也会向服务注册中心注册;
服务消费者还会获取一份可用服务列表,该列表中包含了所有注册到服务注册中心的服务信息(包括服务提供者和自身的信息);
在获得了可用服务列表后,服务消费者通过 HTTP 或消息中间件远程调用服务提供者提供的服务。
服务注册中心(Eureka Server)所扮演的角色十分重要,它是服务提供者和服务消费者之间的桥梁。服务提供者只有将自己的服务注册到服务注册中心才可能被服务消费者调用,而服务消费者也只有通过服务注册中心获取可用服务列表后,才能调用所需的服务。
接下来创建一个项目 EurakaServer7001,启动一个Eureka Server Application服务注册中心。
*添加依赖*
<artifactId>EurakaServer7001</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.3.7.RELEASE</version> <configuration> <mainClass>easy.EueakaServer</mainClass> </configuration> <executions> <execution> <id>repackage</id> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
这里需要进行一些修改:
编辑 C:\Windows\System32\drivers\etc 下的hosts
127.0.0.1 host1.com
127.0.0.1 host2.com
127.0.0.1 host3.com
把本机配成三台服务器(假的),host1.com 等,类似于域名。
*编写配置文件 application.yml:*
spring: application: name: eueakaserver1 server: port: 7001 eureka: instance: hostname: host1.com #eureka服务端的实例名称 client: register-with-eureka: false #false表示不向注册中心注册自己。 fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要 去检索服务 service-url: \#defaultZone: http://host1.com:7001/eureka defaultZone: http://host2.com:7002/eureka,http://host3.com:7003/eureka #集群版本时
*编写启动类**:*
@SpringBootApplication
@EnableEurekaServer //开启 Eureka server,接受其他微服务的注册
public class EueakaServer {
public static void main(String[] args) {
SpringApplication.run(EueakaServer.class,args);
}
}
pom引入依赖 :
<!--引入 Eureka Client 的依赖,将服务注册到 Eureka Server--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
Application.yml需要添加的部分:
eureka: client: service-url: defaultZone: //这里用的是集群 http://host1.com:7001/eureka,http://host2.com:7002/eureka,http://host3.com:7003/eureka instance: instance-id: easybuyproduct7081 prefer-ip-address: true info: app: name: easybuyproduct company: name: this is it build: artifactId: EasyProductPart7081 version: 1.0-SNAPSHOT \#放开监控端点 management: endpoints: web: exposure: include: "*"
pom引入依赖:
<!--引入 Eureka Client 的依赖,可以将服务注册到 Eureka Server--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
Application.yml:
eureka:
client:
register-with-eureka: false #可以不用注册到 Eureka Server,我们这个可以,真正项目中都是微服务 都注册,消费者也同时可以是提供者
fetch-registry: true
service-url:
defaultZone:
//这里用的是集群
http://host1.com:7001/eureka,http://host2.com:7002/eureka,http://host3.com:7003/eureka
入口程序:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration .class}) @EnableEurekaClient public class EasyClientApplication {
public static void main(String[] args) {
SpringApplication.run(EasyClientApplication.class, args);
}
}
*调用方式用服务名*
controller里需要更改一些地方:
因为我用的是集群,故还需两台Eureka服务器。
复制EurakaServer7001为EurakaServer7002
需要更改的一些地方:
Pom:
<artifactId>EurakaServer7002</artifactId> Application.yml: spring: application: name: eueakaserver2 server: port: 7002 eureka: instance: hostname: host2.com #eureka服务端的实例名称(填写自己的主机名) client: register-with-eureka: false #false表示不向注册中心注册自己。 fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要 去检索服务 service-url: \#defaultZone: http://host1.com:7001/eureka defaultZone: http://host1.com:7001/eureka,http://host3.com:7003/eureka #集群版本时(填写除自己外另外两台主机的域名)
再复制EurakaServer7001为EurakaServer7003:
需要更改的一些地方:
Pom:
<artifactId>EurakaServer7003</artifactId> Application.yml: spring: application: name: eueakaserver3 server: port: 7003 eureka: instance: hostname: host3.com #eureka服务端的实例名称 client: register-with-eureka: false #false表示不向注册中心注册自己。 fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要 去检索服务 service-url: \#defaultZone: http://host1.com:7001/eureka defaultZone: http://host1.com:7001/eureka,http://host2.com:7002/eureka #集群版本时(填写除自己外另外两台主机的域名)
*父工程POM添加model*
<modules>
<module>Common</module>
<module>EasyProductPart7081</module>
<module>EasyClient9081</module>
<module>EurakaServer7001</module>
<module>EurakaServer7002</module>
<module>EurakaServer7003</module>
</modules>
分别依次启动三个EurakaServer :
访问localhost:7001:
访问localhost7002:
访问localhost7003:
很明显可以看出每个EurakaServer都有另外两个备用EurakaServer.这时候我们开启商品服务端EasyProductPart7081,服务端会往EurakaServer里注册服务信息,此时再开启商品客户端EasyClient9081,这时候客户端便能从EurakaServer获取到服务信息,进而消费服务。
客户端展示:
此时我们将三台EurakaServer依次关掉,查看客户端的情况:
关掉EurakaServer7001时,
此时客户端不受影响:
再关掉EurakaServer7002时:
此时客户端依旧不受到影响:
最后再关掉最后一台EurakaServer7003:
客户端还是不受到影响:
因此!可以得出结论:EurakaServer开启后,此时再开启服务端,服务端会往EurakaServer注册服务信息,之后再开启客户端后,客户端会从EurakaServer获取到服务信息,进而消费服务。客户端只要第一次从EurakaServer获取到了服务信息之后,之后再关掉EurakaServer服务,客户端不会受到影响!只要总的服务端不关掉,客户端就不会受到影响!!!
这里用到了EurakaServer集群,平时练习使用的时候单节点即可!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。