1.微服务入门
(1).单体架构与分布式架构
单体架构: 将业务的所有功能集中在一个项目中开发,打成一个包部署
优点: 架构简单、部署成本低 ; 缺点: 耦合度高
项目打包部署到Tomcat,用户直接访问。用户量增加后就多部署几台服务器形成集群。随着互联网发展、一个APP或Web通常都用有相当多的模块,因此出现了 分布式架构
分布式架构: 根据业务功能对系统进行拆分,每个业务模块作为独立项目开发,称为一个服务。
优点: 降低服务耦合、有利于服务升级拓展 ;
(2).微服务
微服务是一种经过良好架构设计的分布式架构方案,微服务架构特征:
单一职责: 微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发
面向服务: 微服务对外暴露业务接口
(由于不同模块部署在不同服务器、无法直接调用)
自治: 队独立、技术独立、数据独立、部署独立
隔离性强: 服务调用做好隔离、容错、降级,避免出现级联问题
(避免某个模块宕机造成影响)
单体架构特点?
简单方便,高度耦合,扩展性差,适合小型项目。例如: 学生管理系统分布式架构特点?
松耦合,扩展性好,但架构复杂,难度大。适合大型互联网项目,例如:京东、淘宝微服务:一种良好的分布式架构方案
优点: 拆分粒度更小、服务更独立、耦合度更低缺点:架构非常复杂,运维、监控、部署难度提高
2.服务拆分及远程调用
1.不同微服务,不要重复开发相同业务
2.微服务数据独立,不要访问其它微服务的数据库
3.微服务可以将自己的业务暴露为接口,供其它微服务调用
1.基于RestTemplate发起的http请求实现远程调用
2.http请求做远程调用是与语言无关的调用,只要知道对方的ip、端口、接口路径、请求参数即可。
(1).提供者与消费者
服务提供者: 一次业务中,被其它微服务调用的服务。(提供接口给其它微服务)
服务消费者: 一次业务中,调用其它微服务的服务。(调用其它微服务提供的接口)
一个服务既可以是提供者也可以是消费者,要根据具体的业务和情况来判断
我们服务在请求服务的时候,restTemplate访问的地址是固定的。 可在实际开发中通常都会有好几个环境,开发,测试等等环境。每个环境的地址都在变化 因此出现了几个问题:
1.服务消费者该如何获取服务提供者的地址信息?
2.如果有多个服务提供者,消费者该如何选择?
(2).微服务远程调用Demo
在父模块指定了父 POM
<parent><groupId>org.springframework.boot</groupId><version>3.1.0</version><artifactId>spring-boot-starter-parent</artifactId></parent>
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/db2username: rootpassword: 123456
mybatis:type-aliases-package: com.home.pojoconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplserver:port: 8088
provider实现controller层,services层,和Mapper层,pojo
@RestController
@RequestMapping("/user")
public class userController {@AutowireduserServices userServices;@RequestMapping("/findAll")public usersss findall(){return userServices.findAll();}
}
consumr层只用实现controller层和pojo
@RestController
@RequestMapping("/a")
public class usersssController {@AutowiredRestTemplate restTemplate;@RequestMapping("/b")public usersss findAll(){usersss userssses= restTemplate.getForObject("http://localhost:8088/user/findAll",usersss.class);return userssses;}
}
usersss userssses= restTemplate.getForObject("http://localhost:8088/user/findAll",usersss.class);
3.注册中心 Spring Cloud Eureka
Spirng Cloud Eureka使用Netflix Eureka来实现服务注册与发现(服务治理)。它既包含了服务端组件,也包含了客户端组件,并且服务端与客户端均采用java编写,所以Eureka主要适用于通过java实现的分布式系统,或是JVM兼容语言构建的分布式系统。
Eureka服务端组件:即服务注册中心。它同其他服务注册中心一样,支持高可用配置。依托于强一致性提供良好的服务实例可用性,可以应对多种不同的故障场景。
Eureka客户端组件:主要处理服务的注册和发现。客户端服务通过注册和参数配置的方式,嵌入在客户端应用程序的代码中。在应用程序启动时,Eureka客户端向服务注册中心注册自身提供的服务,并周期性的发送心跳来更新它的服务租约。同时,他也能从服务端查询当前注册的服务信息并把它们缓存到本地,并周期性的刷新服务状态。
注册中心保存提供者提供的服务
客户消费者定期拉去服务列表,需要的话就调用服务提供者,加入IP和端口变化,消费者再拉去一次新的信息即可;
对于注册中心来说,Client和Services都是我的客户端,但是等Client调用Services的时候,Services就是客户端,Client就是服务端;
心跳检测请求是注册中心用来检测服务提供端是否健在的主要依据
1. 整合注册中心Eureka
步骤分三步:
-
第一步:搭建eureka服务,创建eureka_server工程
-
第二步:服务提供者provider_service,注册到eureka注册中心
-
第三步:服务消费者consumer_service,注册到eureka注册中心
4.NACOS
4.1 搭建eureka-server工程
1.创建eureka_server的springboot工程。
2.添加依赖
<properties><spring-cloud.version>2022.0.5</spring-cloud.version><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><modelVersion>4.0.0</modelVersion><artifactId>eureka_server</artifactId><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></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></dependencies></dependencyManagement>
3.在启动类EurekaServerApplication声明当前应用为Eureka服务使用@EnableEurekaServer
注解
@SpringBootApplication
@EnableEurekaServer
public class springBootEurekaServer {public static void main(String[] args) {SpringApplication.run(springBootEurekaServer.class,args);}
}
4.编写配置文件application.yml
server:port: 8080
spring:application:name: consumer
# 注册中心地址
eureka:client:service-url:defaultZone: http://127.0.0.1:8761/eurekaregistry-fetch-interval-seconds: 30
5.启动EurekaServerApplication
6.测试访问地址http://127.0.0.1:8761,如下信息代表访问成功
4.2 服务提供者-注册到eureka
<properties><spring-cloud.version>2022.0.5</spring-cloud.version><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.5</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></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></dependencies> </dependencyManagement>
@SpringBootApplication
@EnableDiscoveryClient
public class springBootProvider {public static void main(String[] args) {SpringApplication.run(springBootProvider.class,args);}
}
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/db2username: rootpassword: 123456 application:name: providerServer mybatis:type-aliases-package: com.home.pojoconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplserver:port: 8088 #配置eureka注册中心的地址 # 注册中心地址 eureka:client:service-url:defaultZone: http://127.0.0.1:8761/eureka # 租约续约间隔时间,默认30秒eureka:instance:lease-renewal-interval-in-seconds: 30
4.3服务消费者-注册到eureka
<properties><spring-cloud.version>2022.0.5</spring-cloud.version><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--当前的项目,是eureka的客户端--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>
</dependencies>
<!--SpringCloud,BOM,依赖清单导入,所有依赖管理的坐标-->
<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></dependencies>
</dependencyManagement>
@SpringBootApplication
@EnableDiscoveryClient
public class springBootConsumerStarter {public static void main(String[] args) {SpringApplication.run(springBootConsumerStarter.class,args);}@Beanpublic RestTemplate restTemplate(){return new RestTemplate();}
}
server:port: 8080 spring:application:name: consumer # 注册中心地址 eureka:client:service-url:defaultZone: http://127.0.0.1:8761/eurekaregistry-fetch-interval-seconds: 30
4.4消费者通过Eureka访问提供者
@RestController
@RequestMapping("/a")
public class usersssController {@AutowiredRestTemplate restTemplate;@Autowiredprivate DiscoveryClient discoveryClient;@RequestMapping("/b")public usersss findAll(){
//discoveryClient 可以拉取注册中心中服务列表//getInstances(服务名),当前服务只有1个,返回值是List集合,获取0索引服务对象ServiceInstance instance = discoveryClient.getInstances("PROVIDERSERVER").get(0);//instance服务对象的实例。获取服务提供者的IP,端口号String host = instance.getHost();int port = instance.getPort();return restTemplate.getForObject("http://"+host+":"+port+"/user/findAll", usersss.class);}
}
# 租约续约间隔时间,默认30秒
eureka.instance.lease-renewal-interval-in-seconds: 30
每过30s注册中心查看服务端是否续租
# 每隔多久获取服务中心列表,(只读备份)
eureka.client.registry-fetch-interval-seconds: 30
每隔30s客户端拉去服务列表
5、SpringCloud Alibaba(Nacos)
5.1 Nacos和Eureka的特点
Eureka的优点包括:
1、简单易用:Eureka框架非常简单易用,便于快速上手和部署。
2、高可用性:Eureka支持多节点部署,并会自动将失效的节点剔除,确保整个系统的高可用性和弹性。
3、动态扩展性:Eureka可以根据实际需求进行扩展,通过添加新的服务提供者可以很容易地增加应用程序的处理能力。
4、易于集成:Eureka可以与Spring Cloud等流行的微服务框架进行无缝集成,从而提供更完善的微服务体系支持。
Eureka的不足之处:
1、Eureka Server 为单点故障问题,虽然可以通过多节点部署来优化和缓解,但是在高并发场景下仍可能成为限制系统扩展的瓶颈。
2、Eureka的服务注册中心本身也需要高可用环境,一旦出现问题,可能影响到整个微服务的正常运行。
Nacos的优点包括:
1、高可用性:Nacos支持多节点部署,通过选举算法实现了高可用和故障转移能力,在节点宕机或网络异常情况下仍能保证整个系统的稳定运行。
2、动态扩展性:Nacos可以根据实际需求进行快速扩展和缩容,支持集群、多数据中心、地域感知等特性。
3、完备的功能支持:Nacos支持服务注册与发现、配置管理、流量管理、DNS解析、存储KV对等功能,并且提供了Web界面和RESTful API等多种方式来使用这些功能。
4、易于集成:Nacos提供了多种语言和框架的集成方案,并且支持Spring Cloud等流行的微服务框架。
总的来说,Nacos是一个功能齐全、易于使用和高可用的分布式服务治理平台,可以为分布式系统提供高效、稳定的运行环境。
5.2 Nacos消费者
使用SpringBoot创建项目,搭建提供者服务(user-service)和消费者服务(consumer-service)。
创建项目user-service
执行创建数据库表,sql脚本
搭建三层架构Mapper、Service、Controller,创建根据id查询用户接口
配置user-service服务
启动2个端口9091、9092服务,并测试服务user-service
创建项目consumer-service
配置RestTemplate对象,注入Spring容器
搭建service层和controller层,通过RestTemplate对象发送请求访问提供者接口
测试消费者服务consumer-service
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
<!--SpringCloud,BOM,依赖清单导入,所有依赖管理的坐标--><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>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring.cloud.alibaba.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>
@RestController
@RequestMapping("user")
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("queryUserById")public User queryUserById(Integer id){return userService.queryUserById(id);}
}
@Service
public class UserServiceImpl implements UserService{@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate DiscoveryClient discoveryClient;@Overridepublic User queryUserById(Integer id) {List<ServiceInstance> instances = discoveryClient.getInstances("provider_user");ServiceInstance serviceInstance = instances.get(0);String url = "http://"+serviceInstance.getHost()+":"+serviceInstance.getPort();System.out.println("url = " + "http://"+url);User user = restTemplate.getForObject(url+"/user/queryUserById?id=" + id, User.class);return user;}
}
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/db2username: rootpassword: 123456application:name: providerNacoscloud:nacos:discovery:server-addr: 127.0.0.1:8848 mybatis:type-aliases-package: com.home.pojoconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
5.3 Nacos服务者
<properties><spring-cloud.version>2022.0.5</spring-cloud.version><spring.cloud.alibaba.version>2022.0.0.0</spring.cloud.alibaba.version><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties>
@RestController
@RequestMapping("/a")
public class usersssController {@AutowiredRestTemplate restTemplate;@Autowiredprivate DiscoveryClient discoveryClient;@RequestMapping("/b/{id}")public usersss findAll(@PathVariable("id") Integer id){
//discoveryClient 可以拉取注册中心中服务列表//getInstances(服务名),当前服务只有1个,返回值是List集合,获取0索引服务对象
// ServiceInstance instance = discoveryClient.getInstances("providerNacos").get(0);//instance服务对象的实例。获取服务提供者的IP,端口号
// String host = instance.getHost();
// int port = instance.getPort();return restTemplate.getForObject("http://providerNacos/user/findAll/"+id, usersss.class);}
}
server:port: 8080 spring:application:name: consumerNacos # 注册中心地址cloud:nacos:discovery:server-addr: 127.0.0.1:8848
6.负载均衡
将任务均匀分配
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency>
server:
port: ${port:9091}
构建集群
@RestController
@RequestMapping("/user")
public class userController {@Autowiredhome.services.userServices userServices;@Value("${server.port}")private Integer port;@GetMapping("/findAll/{id}")public usersss findall(@PathVariable("id") Integer id){usersss usersss1= userServices.findAll(id);usersss1.setPort(port);return usersss1;}
}
6.1 修改负责均衡算法
public class CustomLoadBalancerConfiguration {
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
@Configuration
@LoadBalancerClients(value = {
@LoadBalancerClient(name = "providerUser" , configuration = CustomLoadBalancerConfiguration.class) // 将负载均衡算法应用到指定的服务提供方中
})
public class RestTemplateConfiguration {@Bean
@LoadBalanced // 让RestTemplate具有负载均衡的能力
public RestTemplate restTemplate() {
return new RestTemplate() ;
}
}
负载均衡算法可以改变调用顺序;