赞
踩
单体架构:将业务的所有功能集中在一个项目中开发,打成一个包部署。
优点:
部署简单
部署成本低
缺点:
耦合度高
扩展性差
分布式架构:根据业务功能对系统进行拆分,每个业务模块作为独立项目开发,称为一个服务。
优点:
降低服务耦合度
有利于服务升级扩展
缺点:
架构复杂
难度大
微服务: 微服务是一种经过良好架构设计的分布式架构方案,微服务架构特征:
优点: 拆分粒度更小、服务更独立、耦合度更低
缺点: 架构非常复杂,运维、监控、部署难度提高
(Dubbo、SpringCloud、SpringCloudAlibaba)微服务技术对比:
企业需求:
SpringCloud是目前国内使用最广泛的微服务架构框架。它集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配。
官网地址:Spring Cloud
服务拆分注意事项:
通过@Bean注解将RestTemplate对象交给spring管理,在需要的地方注入RestTemplate对象
调用方法:
//利用RestTemplate发送http请求
//url路径
String url = "http://localhost:8081/user/"+ order.getUserId();
//发送http请求,实现远程调用
User user = restTemplate.getForObject(url, User.class);
服务调用出现的问题 : 1.调用服务地址硬编码 2.多个服务如何选择 3.怎么知道服务提供者是否是健康状态
Eureka的作用:
消费者如何向服务提供者提供具体信息的
如果多个提供者,消费者该如何选择
消费者如何感知服务器提供者的健康状态
1.创建项目引入依赖
<!-- eureak服务端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
2.编写启动类,添加@EnableEurekaServer注解
3.添加application.yml文件
server:
port: 10086 #服务端口
spring:
application:
name: eurekaserver
eureka:
client:
service-url: #eureka的地址信息
deafultZone: http://127.0.0.1:10086/eureka
启动项目后方法10086端口就可以到eureka的界面
将服务注册到eureka的步骤:
1.在服务项目中引入spring-cloud-starter-netflix-eureka-client的依赖
<!-- eureka客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.在application.yml文件,编写配置
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false
username: root
password: ******
driver-class-name: com.mysql.jdbc.Driver
application:
name: userService #user服务名称
mybatis:
type-aliases-package: cn.itcast.user.pojo
configuration:
map-underscore-to-camel-case: true
logging:
level:
cn.itcast: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS
eureka:
client:
service-url: #eureka的地址信息
defaultZone: http://127.0.0.1:10086/eureka
1.在请求路径上面把路径换成我们注册服务的名称
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
//利用RestTemplate发送http请求
//url路径(将路径换成需要的服务的名称)
String url = "http://userservice/user/"+ order.getUserId();
//发送http请求,实现远程调用
User user = restTemplate.getForObject(url, User.class);
order.setUser(user);
// 4.返回
return order;
}
2.加上RestTemplate的Bean上面加 @LoadBalanced 注解
ribbon的负载均衡策略
默认是轮询方式
1.代码方式:在消费端的Application中,定义一个新的IRule
@Bean
public IRule randomRule(){
return new RandowRule();
}
2.配置文件方式:配置文件只是更改一个服务的负载,不是全局
userservice:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
Ribbon默认是采用懒加载,即第一次加载时才会去创建LoadBalanceClient,请求时间会很长。而解饿加载则会在项目启动时创建,降低第一次访问的耗时。
ribbon:
eager-load:
enabled: true #开启解饿加载
clients:
-userservice #指定服务名称,多个服务需要在下面加 -
Nacos是阿里巴巴的产品,现在是SpringCloud中的一个组件。相比Eureka功能更加丰富。
Windows启动nacos命令:startup.cmd -m standalone
1.在Cloud-demo父工程中添加Spring-cloud-alilbaba 的管理依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
2.添加nacos的客户端依赖
<!-- nacos客户端依赖包-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
3.改写application.yml 文件,向yml文件中加入nacos的服务地址配置
application:
name: userservice #user服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 #nacos服务地址
添加服务集群属性
修改application.yml
cloud:
nacos:
discovery:
server-addr: localhost:8848 #nacos服务地址
cluster-name: SH #集群名称,Hz代指杭州
更改服务访问规则(NacosRule负载均衡规则)
userservice:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.nacos.ribbon.NacosRule
NacosRule负载均衡规则 : 集群优先
1.在nacos网站去创建namespace命名空间
2.配置服务所在命名空间,在application.yml文件中修改
cloud:
nacos:
discovery:
server-addr: localhost:8848
cluster-name: HZ
namespace: 359d6479-2271-43fa-9ca3-9cf1f1082906 #dev环境(复制我们创建好的命名空间Id)
不同nameSpace下的服务不可见(不能访问)
Nacos和eureka的区别
配置更改热更新
统一配置管理
1.引入Nacos的配置管理客户端依赖:
<!-- nacos的配置管理依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2.在服务提供者中的resource目录中添加一个bootstrap.yml文件,这个文件是引导文件,优先级高于application.yml文件
spring:
application:
name: userservice #服务名称
profiles:
active: dev #环境
cloud:
nacos:
discovery:
server-addr: localhost:8848 #nacos地址
config:
file-extension: yaml #文件后缀名
将application.yml文件中重复的配置删掉
配置自动刷新
方式一:在@Value注入的变量所在类上添加注解@RefreshScope
方式二:使用@ConfigurationProperties注解
@Data
@Component
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties {
private String dateformat;
}
多环境配置共享优先级:
服务名-profile.yaml > 服务名.yaml > 本地配置
热更新的Springboot实现
1.导入jar包
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-starter</artifactId>
<version>0.2.11</version>
</dependency>
2.更改application.yaml文件
nacos:
config:
data-id: jc-club-oss #用于指定要获取的配置数据的ID或名称。
secret-key: nacos # 用于进行访问控制的密钥,用于对配置中心的访问进行认证和授权。
access-key: nacos # 用于进行访问控制的密钥,用于对配置中心的访问进行认证和授权。
group: DEFAULT_GROUP #用于指定配置数据所属的分组,这样可以更好地组织和管理配置。
type: yaml #指定配置数据的类型,例如 YAML、Properties 等。
server-addr: http://117.72.14.166:8848/ #指定Nacos配置中心的地址,包括主机名和端口。
auto-refresh: true #标识是否自动刷新配置内容,当配置中心的配置发生变化时,客户端是否自动更新配置。
remote-first: true #当配置中心不可用时,是否优先使用本地缓存的配置。
bootstrap:
enable: true #是否启用Bootstrap配置,Bootstrap配置是在Spring应用程序启动时首先加载的一组配置。
3.使用@NacosValue注解
@NacosValue(value = "${storage.service.type}",autoRefreshed = true)
private String storageType;
@GetMapping("/testNacos")
public String testNacos() {
return storageType;
}
出现 failed to req API:/nacos/v1/ns/instance after all servers([localhost:8848]) tried: java.net.ConnectException: Connection refused: connect 异常
因为同时导入了Nacos的配置中心和注册中心的依赖导致
Nacos配置中心依赖 -config 注册中心 -discovery
使用之前的RestTemplate方式调用会出现一些问题
Feign是一声明式的http客户端,作用是帮助我们优雅的实现http请求的发送。
1.引入Feign依赖
<!-- feign客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
2.在启动类上添加注解开启Feign的功能
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
@EnableFeignClients
3.编写Feign客户端
@FeignClient("userservice")
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
注意:若出现 Load balancer does not have available server for client: userservice 错误 需要查看服务的命名空间是否一致
微服务之间用openfeign调用要想保证微服务之间的用户上下信息一致需要添加feign的拦截器
@Component
public class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
if (!ObjectUtils.isEmpty(request)) {
String loginId = request.getHeader("loginId");
if (StringUtils.isNotBlank(loginId)){
requestTemplate.header("loginId",loginId);
}
}
}
}
同时将FeignRequestInterceptor注册成Bean交给Spring管理
@Configuration
public class FeignConfiguration {
@Bean
public RequestInterceptor requestInterceptor(){
return new FeignRequestInterceptor();
}
}
这样其他微服务就可以通过拦截器拿到loginId
配置日志级别方式一(修改配置文件的方式):
feign:
client:
config:
default:
# 设置日志记录级别,其取值共有none、basic、headers、full
loggerLevel: FULL
配置日志级别方式二(声明Bean的方式):
public class DefaultFeignConfiguration {
@Bean
public Logger.Level logLevel(){
return Logger.Level.BASIC;
}
}
全局有效
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)
具体服务有效
@FeignClient(value = "userservice",configuration = DefaultFeignConfiguration.class)
使用连接池代替默认的URLConnection:
Feign添加HttpClient的支持依赖:
<!-- 引入HttpClient依赖-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
配置application.yml文件:
feign:
httpclient:
enabled: true #支持httpClient的开关
max-connections: 200 #最大连接数
max-connections-per-route: 50 #单个路径的最大连接数
Feign的优化:
Feign的最佳实践
方式二:
新建module并引入feign的starter的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
将feign的客户端和实体类抽取到新建的module中
在需要使用服务的pom文件中引入依赖
修改组件有关的import部分,改成使用新建module中的包
Springcloud 中网关的实现包括两种:
1.创建一个module,引入SpringCloudGateway的依赖和Nacos的服务发现依赖:
<!-- nacos服务发现依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 网关gateway依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
2.编写路由配置以及Nacos地址
server:
port: 10010 #网关端口
spring:
application:
name: gateway #服务名称
cloud:
nacos:
server-addr: localhost:8848
gateway:
routes: #网关路由配置
- id: user-service #路由Id,自定义,只要唯一即可
#uri: http://127.0.0.1:8081 #路由的目标地址http是固定地址
uri: lb://userservice #路由目标地址 lb就是负载均衡,后面跟服务名称
predicates: #路由断言,也就是判断请求是否符合路由规则的条件
- Path=/user/** #按照路径匹配,只要以/user/开头就符合条件
- id: order-service
uri: lb://orderservice
predicates:
- Path=/order/**
总结:
网关搭建步骤:
创建项目,引入nacos服务发现和gateway依赖
配置application.yml,包括服务基本信息、nacos地址、路由
路由配置包括:
路由id:路由的唯一标示
路由目标(uri):路由的目标地址,http代表固定地址,lb代表根据服务名负载均衡
路由断言(predicates):判断路由的规则,
路由过滤器(filters):对请求或响应做处理
我们在配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断的条件
例如Path=/user/**是按照路径匹配,这个规则是由
org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory
类来
处理的,像这样的断言工厂在SpringCloudGateway还有十几个:
名称 | 说明 | 示例 |
---|---|---|
After | 是某个时间点后的请求 | - After=2037-01-20T17:42:47.789-07:00[America/Denver] |
Before | 是某个时间点之前的请求 | - Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai] |
Between | 是某两个时间点之前的请求 | - Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver] |
Cookie | 请求必须包含某些cookie | - Cookie=chocolate, ch.p |
Header | 请求必须包含某些header | - Header=X-Request-Id, \d+ |
Host | 请求必须是访问某个host(域名) | - Host=.somehost.org,.anotherhost.org |
Method | 请求方式必须是指定方式 | - Method=GET,POST |
Path | 请求路径必须符合指定规则 | - Path=/red/{segment},/blue/** |
Query | 请求参数必须包含指定参数 | - Query=name, Jack或者- Query=name |
RemoteAddr | 请求者的ip必须是指定范围 | - RemoteAddr=192.168.1.1/24 |
Weight | 权重处理 |
GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理:
名称 | 说明 |
---|---|
AddRequestHeader | 给当前请求添加一个请求头 |
RemoveRequestHeader | 移除请求中的一个请求头 |
AddResponseHeader | 给响应结果中添加一个响应头 |
RemoveResponseHeader | 从响应结果中移除有一个响应头 |
RequestRateLimiter | 限制请求的流量 |
下面我们以AddRequestHeader 为例来讲解。
需求:给所有进入userservice的请求添加一个请求头:Truth=itcast is freaking awesome!
只需要修改gateway服务的application.yml文件,添加路由过滤即可:
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://userservice
predicates:
- Path=/user/**
filters: # 过滤器
- AddRequestHeader=Truth, Itcast is freaking awesome! # 添加请求头
当前过滤器写在userservice路由下,因此仅仅对访问userservice的请求有效。
如果要对所有的路由都生效,则可以将过滤器工厂写到default下。格式如下:
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://userservice
predicates:
- Path=/user/**
default-filters: # 默认过滤项
- AddRequestHeader=Truth, Itcast is freaking awesome!
需求:定义全局过滤器,拦截请求,判断请求的参数是否满足下面条件:
参数中是否有authorization,
authorization参数值是否为admin
如果同时满足则放行,否则拦截
实现:
在gateway中定义一个过滤器:
package cn.itcast.gateway.filters;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1.获取请求参数
MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
// 2.获取authorization参数
String auth = params.getFirst("authorization");
// 3.校验
if ("admin".equals(auth)) {
// 放行
return chain.filter(exchange);
}
// 4.拦截
// 4.1.禁止访问,设置状态码
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
// 4.2.结束处理
return exchange.getResponse().setComplete();
}
}
请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter
请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器:
排序的规则:
详细内容,可以查看源码:
org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters()
方法是先加载defaultFilters,然后再加载某个route的filters,然后合并。
org.springframework.cloud.gateway.handler.FilteringWebHandler#handle()
方法会加载全局过滤器,与前面的过滤器合并后根据order排序,组织过滤器链
在gateway服务的application.yml文件中,添加下面的配置:
spring:
cloud:
gateway:
# 。。。
globalcors: # 全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期
网关提示503错误,并且配置的地址信息都符合 查看是否配置的负载均衡的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。