Java学习Day60:微服务总结!(有经处无火,无火处无经)
1、技术版本
jdk:17及以上
-如果JDK8
springboot:3.1及其以上
-版本2.x
springFramWork:6.0及其以上
-版本5.x
springCloud:2022.0.5
-版本格林威治或者休斯顿
2、模拟springcloud
父模块指定父pom
<parent>
<groupId>org.springframework.boot</groupId>
<version>3.1.0</version>
<artifactId>spring-boot-starter-parent</artifactId>
</parent>
配置provider的application.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db2
username: root
password: 123456
application:
name: providerServer
mybatis:
type-aliases-package: com.home.pojo
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
server:
port: 8088
provider实现controller层,services层,和Mapper层,pojo
provider的pom
<properties>
<spring-cloud.version>2022.0.5</spring-cloud.version>
<properties>
spring-boot-starter-web
mysql-connector-j
lombok
mybatis-spring-boot-starter
mybatis-plus-spring-boot3-starter
<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>
consumr层只用实现controller层和pojo
consumer的application.yml
server:
port: 8080
spring:
application:
name: consumer
pom
<properties>
<spring-cloud.version>2022.0.5</spring-cloud.version>
<properties>
spring-boot-starter-web
lombok
<!--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>
consumer调用provider的方法
usersss userssses= restTemplate.getForObject("http://localhost:8088/user/findAll",usersss.class);
return userssses;
访问consumer即可调用provider的方法
思考:
1.硬URL的端口号(端口变化,服务宕机
2.负载均衡无法实现
3.无返回信息
4.优化RestTemplate
5.多服务权限拦截如何实现?怎么保证所有微服务服务的安全性?
6.优化众多微服务的配置文件
3.注册中心eureka
Eureka服务器:
pom
<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>
application.yml
#启动服务器端口
server:
port: 8761
#应用程序名字
spring:
application:
name: eurekaServer
#EurekaServer的地址,现在是自己的地址,如果是集群,需要写其它Server的地址。
eureka:
client:
fetch-registry: false
register-with-eureka: false
service-url:
defaultZone: http://127.0.0.1:8761/eureka
启动类中标志:@EnableEurekaServer
@SpringBootApplication
@EnableEurekaServer
public class springBootEurekaServer {
public static void main(String[] args) {
SpringApplication.run(springBootEurekaServer.class,args);
}
}
Eureka服务端组件:
在服务提供者provider_service工程中添加Eureka客户端依赖
<!--eureka客户端starter-->
<dependencies>
<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>
在启动类上开启Eureka客户端发现功能@EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient // 开启Eureka客户端发现功能
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class,args);
}
}
注册到注册中心
修改配置文件:spring.application.name指定应用名称,作为服务ID使用
server:
port: 8088
#配置eureka注册中心的地址
# 注册中心地址
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8761/eureka
# 租约续约间隔时间,默认30秒
eureka:
instance:
lease-renewal-interval-in-seconds: 30
Eureka客户端组件
在服务消费者consumer_service工程中添加Eureka客户端依赖
<!-- Eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--SpringCloud所有依赖管理的坐标-->
<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>
注册到注册中心
# 注册中心地址
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8761/eureka
registry-fetch-interval-seconds: 30
在启动类开启Eureka客户端@EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient
public class springBootConsumerStarter {
public static void main(String[] args) {
SpringApplication.run(springBootConsumerStarter.class,args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
配置完成后执行流程
-
通过注册中心客户端对象DiscoveryClient,获取Eureka中注册的user-service实例列表
-
获取user-service服务实例对象
-
从实例对象中获取host地址和端口,拼接请求地址
-
使用RestTemplate发送请求
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);
}
}
4.Nacos
相比于erueka
Nacos消费者配置:
<properties>
<spring.cloud.alibaba.version>2022.0.0.0</spring.cloud.alibaba.version>
<properties>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<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>
配置Nacos注册中心地址
server:
port: 8080
spring:
application:
name: consumerNacos
# 注册中心地址
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
Nacos服务端配置:
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<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>
application.yml不变
负载均衡:
在consumer中配置即可
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
在启动类配置
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
问题:需要编写类似的大量重复代码,格式基本相同,无非参数不一样!
5.OpenFeign
Feign 的英文表意为“假装,伪装,变形”, 是一个http请求调用的轻量级框架,是以Java接口的方式发送Http请求,而不用像Java中通过封装HTTP请求url的方式直接调用。Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,这种请求相对而言比较直观。
Feign被广泛应用在Spring Cloud 的解决方案中,是学习基于Spring Cloud 微服务架构不可或缺的重要组件。
需要导入:
<!--伪装调用jar-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
启动引导类加@EnableFeignClients注解
@SpringBootApplication
@EnableFeignClients
public class SpringBootFeignConStart {
public static void main(String[] args) {
SpringApplication.run(SpringBootFeignConStart.class,args);
}
}
编写FeignClient接口,使用SpringMVC的注解
@FeignClient("providerUser")
public interface FeignUserService {
@RequestMapping("feign/eignQueryUserById")
User feignQueryUserById(@RequestParam("id") Integer id);
}
在Controller中注入Feign接口,直接调用,无需实现类,但是GetMaping的Restful风格必去和调用provider的方法相同且传参方式也相同!!!
访问接口测试!
支持熔断(服务降级)、配置日志级别、请求压缩和响应压缩
6.网关GateWay
所有微服务的统一入口。Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。
-
Route(路由):这是网关的基本模块。它由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配。
-
Predicate(断言):这是一个 Java 8 的 Predicate。输入类型是一个 ServerWebExchange。我们可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。
-
Filter(过滤器):这是org.springframework.cloud.gateway.filter.GatewayFilter的实例,我们可以使用它修改请求和响应。
创建SpringBoot工程gateway_server
<!--引入网关的jar包,不能引web jar包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</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>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>
application.yml
定义端口号,名称,注册到nacos
这是为当前路由规则指定一个 唯一的标识符
lb://
是 负载均衡(Load Balancer)的前缀,意味着请求将通过负载均衡机制路由到一个注册在 Eureka、Consul 或其他注册中心的服务。consumerOpenFeign
是服务的名称,这个名称需要与注册中心中服务的名称一致。Spring Cloud Gateway 会从服务注册中心获取该服务的实际地址(例如:http://consumerOpenFeign:8080
),并且通过负载均衡器将请求转发到该服务。
predicates
用于定义请求的 匹配条件,即哪些请求应该由当前的路由规则来处理。在这个例子中,使用了 Path=/**
,表示匹配所有的路径。
过滤器隐藏usertwo,访问时不用书写,避免接口地址暴露
server:
port: 80
spring:
application:
name: gateway
cloud:
nacos:
server-addr: 127.0.0.1:8848
#配置网关
gateway:
routes:
- id: orderService #自定义路由的唯一标识符
uri: lb://consumerOpenFeign #服务名字,从注册中心通过服务名称来获取接口
predicates:
- Path=/** #拦截浏览器的请求地址
filters:
- PrefixPath=/usertwo
自定义全局过滤器【重点】
token:登录成功后,根据登录用户名等(用户ID),生成一个属于这个用户自己的密钥,就称为Token。下次用户访问的时候,带着Token来请求服务器。拿Token值反解析出用户数据来(无论是解析失败,还是根本就没有Token)拦截器不能放行。登录成功的时候,产生一个加密后字符串:Token响应回浏览器的响应头。浏览器再次请求的时候,携带Token来访问服务器,Token会放在浏览器的请求头中。
大部分都是异步请求,axios拦截器
响应的时候,获取响应头中的token值。请求的时候,在请求头中添加token值。
token值存储在浏览器的缓存中,缓存对象 localStorage, setItem存储键值对,getItem传递键获取值
nacos注册和配置中心
nacos单独组件,启动就可以了,默认端口8848
微服务配置nacos注册中心的地址
spring:
cloud:
nacos:
server-addr: 127.0.0.1:8848
sentinel流量防卫兵
<!--熔断的支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<!--配置哨兵-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
配置文件
server:
port: 8080
spring:
application:
name: consumerOpenFeign
cloud:
#哨兵
sentinel:
transport:
dashboard: 127.0.0.1:8086
nacos:
server-addr: 127.0.0.1:8848
openfeign:
#feign熔断支持
circuitbreaker:
enabled: true
在方法上定义哨兵
@GetMapping("/selectusertwo")
@SentinelResource(value = "selectusertwo",
fallback ="selectusertwoFallBack",
blockHandler = "selectusertwoHandler")
public usersss selectusertwo(@RequestParam("id") Integer id){
if (id<4){
throw new RuntimeException("请输入正确ID!");
}
return userServicesTwo.selectusertwo(id);
}
fallback:限流
blockHandler:熔断