1.统一配置管理

1.统一配置管理流程
1.引入Nacos的配置管理客户端依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2.在userservice中的resource目录添加一个bootstrap.yml文件,这个文件是引导文件,优先级高于application.yml
spring:
application:
name: userservice # 服务名称
profiles:
active: dev #开发环境,这里是dev
cloud:
nacos:
server-addr: localhost:8848 # Nacos地址
config:
file-extension: yaml # 文件后缀名
3.在nacos中编写配置文件
4.在user-service中将pattern.dateformat这个属性注入到UserController中做测试
@Value("${pattern.dateformat}")
private String dateformat;
总结:1.在微服务的pom文件中添加nacos的config依赖。
2.创建bootstrap.yml,配置nacos地址,以及配置在nacos中的配置文件名。
3.在nacos中添加配置文件。
1.配置自动刷新:
配置文件变更后,微服务无需重启就可以感知
方式一:
1.在@Value注入的变量所在类上添加注解@RefreshScope
@RefreshScope
public class UserController {
@Value("${pattern.dateformat}")
private String dateformat;
方式二:
2.使用@ConfigurationProperties注解
1.创建一个类用于接收属性。
@Data
@Component
/*
* 热更新2
* */
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties {
private String dateformat;
private String envShareValue;
}
2.在controller中注入调用
@Autowired
private PatternProperties patternProperties;
@GetMapping("now")
public String now(){
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat()+"==="+patternProperties.getEnvShareValue()));
总结:Nacos配置更改后实现热更新的方法有两个:
1.使用@Value注解注入,结合@RefreshScope来刷新。
2.通过@ConfigurationPreperties注入,自动刷新。
注意:
1.不是所有的配置都适合放在配置中心,维护起来比较麻烦
2.建议将一些关键参数,需要运行时调整的参数放到nacos配置中心,一般都是自定义配置。
2.多环境共享:
配置文件的格式:
格式1.[spring.application.name]-[spring.profiles.active].yaml
格式2.[spring.application.name].yaml
无论profile如何变化,[spring.application.name].yaml这个文件一定会加载,因此多环境共享配置可以写入这个文件
优先级:

总结:
配置文件有两种格式:
1.[服务名]-[spring.profile.active].yaml,环境配置
2.[服务名].yaml,默认配置,多环境共享
优先级:
[服务名]-[环境].yaml >[服务名].yaml > 本地配置
3.nacos集群搭建
步骤:
1.搭建MySQL集群并初始化数据库表
2.下载解压nacos
3.修改集群配置(节点信息)、数据库配置
4.分别启动多个nacos节点
5.nginx反向代理
2.http客户端Feign
1.Feign替代RestTemplate
String url = " http://userservice/user/ "+ order.getUserId();User user = restTemplate.getForObject(url, User. class);
RestTemplate方式的代码可读性差,编程体验不统一
参数复杂URL难以维护
1.Feign的使用步骤:
1.引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.在order-service的启动类添加注解开启Feign的功能:
/*
* 开启自动配置的开关
* */
@EnableFeignClients
public class OrderApplication {
3.编写Feign客户端接口:(主要是基于SpringMVC的注解来声明远程调用的信息)
@FeignClient("userservice")
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
4.用Feign客户端代替RestTemplate
@Service
public class OrderService {
@Autowired
private UserClient userClient;
@Autowired
private OrderMapper orderMapper;
public Order queryOrderById(Long orderId){
//查询订单
Order order = orderMapper.findById(orderId);
//用Feign远程调用
User user = userClient.findById(order.getUserId());
//封装user到Order
order.setUser(user);
//返回
return order;
}
}
order-service
@RestController
@RequestMapping("order")
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("{orderId}")
public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
// 根据id查询订单并返回
return orderService.queryOrderById(orderId);
}
}
user-service
@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id,@RequestHeader(required = false,value = "Truth") String t) {
System.out.println(t);
return userService.queryById(id);
}
总结:1.引入依赖
2.添加@EnableFerignClients注解
3.编写FeignClient接口
4.使用FeignClient中定义的方法代替RestTemplate
2.自定义配置

方式一:(全局生效)
feign:
client:
config:
default:
loggerLevel: FULL
方式二:(局部生效)
feign:
client:
config:
userservice:
loggerLevel: FULL
方式三:java代码方式需要先声明一个Bean
1.需要先声明一个Bean
public class FeignClientConfiguration {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.NONE;
}
}
1.如果是全局配置,则把它放到@EnableFeignClients这个注解中:
@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)
/*
* 开启自动配置的开关
* clients:指定扫描包
* */
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
2.如果是局部变量就放到@FeignClien这个注解中:
@FeignClient(value="userservice",configuration = FeignClientConfiguration.class)
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
总结:1.配置文件方式:
feign.client.config.XXX.loggerLevel:
XXX为default则代表全局
XXX是服务名称,则代表某个服务
2.java代码配置:
@EnableFeignClients:代表全局生效
@FeignClient:代表某个服务
3.Feign使用优化
Feign底层客户端实现
URLConnection:默认实现,不支持连接池
Apache HttpClient:支持连接池
OKHttp:支持连接池
如何优化?
1.使用支持连接池的连接
2.日志级别,最好是basic和none
Feign添加HttpClient连接:
1.引入依赖:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
2.配置连接池:
feign:
httpclient:
enabled: true #开启feign对HttpClient的支持
max-connections: 200 #最大连接数
max-connections-per-route: 50 #每个路径的最大连接数
总结:1.日志尽量用basic
2.使用HttpClient或OKHttp代替URLConnection
1.引用feign-httpClient依赖
2.配置文件开启httpClient功能,设置连接池参数
4.最佳实践



实现最佳实践方法二的步骤如下:
1.首先创建一个module,命名为feign-api,然后引用feign的starter依赖
2.将order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中
3.在order-service中引入feign-api的依赖
4.修改order-service中的所有与上述三个组件有关的import部分,改成导入feign-api中的包
问题:当定义的FeignClient不在SpringBootApplication的扫描包范围时,这些FeignClient无法使用。
解决:
1.指定FeignClient所在包
@EnableFeignClients(basePackages = "cn.itcast.feign.clients",defaultConfiguration = FeignClientConfiguration.class)
2.指定FeignClient字节码
@EnableFeignClients(clients = UserClient.class,defaultConfiguration = FeignClientConfiguration.class)
/*
* 开启自动配置的开关
* clients:指定扫描包
* */
public class OrderApplication {
统一网关Gateway:
1.网关功能:
1.身份认证和权限校验
2.服务路由,负载均衡
3.请求限流

Zuul是基于Servlet的实现,属于阻塞式编程。而SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能。
gateway使用:
1.创建gateway模块,引入SpringCloudGateway的依赖和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>
</dependencies>
2.编写路由配置以及nacos地址
server:
port: 10010 #网关端口
spring:
application:
name: gateway #服务名称
cloud:
nacos:
server-addr: localhost:80 #nacos地址
gateway:
routes: #网关路由配置
- id: user-service #路由id,自定义,只要唯一即可
# uri: http://127.0.0.1:8081 #路由的目标地址(定死了)
uri: lb://userservice #路由的目标地址,lb就是负载均衡,后面跟服务名
predicates: #路由断言,也就是判断请求是否符合路由规则的条件
- Path=/user/** #这个是按路径匹配,只要以/user/开头就符合要求。

总结:路由配置包括:
1.路由id:路由的唯一标示
2.路由目标uri:路由的目标地址,http代表固定地址,lb代表根据服务名负载均衡
3.路由断言:判断路由的规则,符合则转发到路由目的地
4.路由过滤器:对请求或响应做处理
断言工厂:

过滤器工厂:


1.过滤器(只对当前请求生效)
routes: #网关路由配置
- id: user-service #路由id,自定义,只要唯一即可
# uri: http://127.0.0.1:8081 #路由的目标地址(定死了)
uri: lb://userservice #路由的目标地址,lb就是负载均衡,后面跟服务名
predicates: #路由断言,也就是判断请求是否符合路由规则的条件
- Path=/user/** #这个是按路径匹配,只要以/user/开头就符合要求。
filters:
- AddRequestHeader=Truth,Itcast is freaking awesome!
2.默认过滤器(对所有的路由都生效)
- id: order-service
uri: lb://orderservice
predicates:
- Path=/order/**
default-filters: #默认过滤器,会对所有路由都生效。
- AddRequestHeader=Truth,Itcast is freaking awesome! #添加请求头
全局过滤器:
全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。
区别在于GatewayFilter通过配置定义,处理逻辑是固定的。而GlobalFilter的逻辑需要自己写代码实现。
定义方式是实现GlobalFilter接口。
//@Order(-1)//==Ordered
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
/**
* 处理当前请求,有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器处理
* @param exchange 请求上下文,里面可以获取Request,Response等信息
* @param chain 用来把请求委托给下一个过滤器
* @return {@code Mono<Void>} 返回标示当前过滤器业务结束
*/
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain){
//1.获取请求参数
ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String, String> queryParams = request.getQueryParams();
//2.获取参数中的authorization参数
String authorization = queryParams.getFirst("authorization");
//3.判断参数值是否等于admin
if (authorization .equals("admin")){
//4.是,放行
return chain.filter(exchange);
}
//5.否,拦截
// 5.1设置状态码
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);//未登录
// 5.2拦截请求
return exchange.getResponse().setComplete();
}
@Override
public int getOrder() {
return -1;
}
}
总结:
全局过滤器的作用:
对所有路由都生效的过滤器,并且可以自定义处理逻辑
实现步骤:
1.实现GlobalFilter接口
2.添加@Order注解或实现Order接口
3.编写处理逻辑
1.每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。
2.GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定
3.路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。
4.当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。

跨域问题:
跨域:域名不一致就是跨域,主要包括:
域名不同: www.taobao.com 和 www.taobao.org 和 www.jd.com 和 miaosha.jd.com
域名相同,端口不同:localhost: 8080和localhost8081
跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题
解决方案:CORS
网关处理跨域问题采用的同样是CORS方案,并且只需要简单配置即可实现:
gateway:
globalcors: #全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
cors-configurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
- "http://www.leyou.com"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" #允许在请求中携带的头信息
allowCredentials: true #是否允许携带cookie
maxAge: 360000 #这次跨域检测的有效期