南京网站开发推南京乐识,网站建设准备工作,网架报价明细表,wordpress自动格式化代码1.Nacos 配置管理 Nacos 除了可以做注册中心#xff0c;同样可以做配置管理来使用。 1.1.统一配置管理 当微服务部署的实例越来越多#xff0c;达到数十、数百时#xff0c;逐个修改微服务配置就会让人抓狂#xff0c;而且很容易出错。我们需要一种统一配置管理方案#x…1.Nacos 配置管理 Nacos 除了可以做注册中心同样可以做配置管理来使用。 1.1.统一配置管理 当微服务部署的实例越来越多达到数十、数百时逐个修改微服务配置就会让人抓狂而且很容易出错。我们需要一种统一配置管理方案可以集中管理所有实例的配置。 Nacos 一方面可以将 配置集中管理另一方可以在配置变更时及时通知微服务实现配置的 热更新。 1.1.1.在nacos中添加配置文件 如何在 nacos 中管理配置呢 然后在弹出的表单中填写配置信息 注意 项目的核心配置需要热更新的配置才有放到 nacos 管理的必要。基本不会变更的一些配置还是保存在微服务本地比较好。 1.1.2.从微服务拉取配置 微服务要拉取 nacos 中管理的配置并且与本地的 application.yml 配置合并才能完成项目启动。 但如果尚未读取 application.yml又如何得知 nacos 地址呢 因此 spring 引入了一种新的配置文件bootstrap.yaml 文件会在 application.yml 之前被读取流程如下 1引入 nacos-config 依赖 首先在user-service服务中引入nacos-config的客户端依赖 !--nacos配置管理依赖--
dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-config/artifactId
/dependency2添加 bootstrap.yaml 然后在 user-service 中添加一个 bootstrap.yaml 文件内容如下 spring:application:name: userservice # 服务名称profiles:active: dev #开发环境这里是dev cloud:nacos:server-addr: localhost:8848 # Nacos地址config:file-extension: yaml # 文件后缀名这里会根据 spring.cloud.nacos.server-addr 获取 nacos 地址再根据 ${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}作为文件id来读取配置。 本例中就是去读取userservice-dev.yaml 3读取 nacos 配置 在 user-service 中的 UserController 中添加业务逻辑读取 pattern.dateformat 配置 1.2.配置热更新 我们最终的目的是修改 nacos 中的配置后微服务中无需重启即可让配置生效也就是 配置热更新。 要实现配置热更新可以使用两种方式 1.2.1.方式一 在 Value 注入的变量所在类上添加注解 RefreshScope 完整代码 package cn.itcast.user.web;import cn.itcast.user.config.PatternProperties;
import cn.itcast.user.pojo.User;
import cn.itcast.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;Slf4j
RestController
RequestMapping(/user)
public class UserController {Autowiredprivate UserService userService;Autowiredprivate PatternProperties patternProperties;GetMapping(now)public String now(){return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat()));}// 略
}1.2.2.方式二 使用 ConfigurationProperties 注解代替 Value 注解。 在 user-service 服务中添加一个类读取 patterrn.dateformat 属性 package cn.itcast.user.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;Component
Data
ConfigurationProperties(prefix pattern)
public class PatternProperties {private String dateformat;
}在 UserController 中使用这个类代替 Value 完整代码 package cn.itcast.user.web;import cn.itcast.user.config.PatternProperties;
import cn.itcast.user.pojo.User;
import cn.itcast.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;Slf4j
RestController
RequestMapping(/user)
public class UserController {Autowiredprivate UserService userService;Autowiredprivate PatternProperties patternProperties;GetMapping(now)public String now(){return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat()));}// 略
}1.3.配置共享 其实微服务启动时会去 nacos 读取多个配置文件例如 [spring.application.name]-[spring.profiles.active].yaml例如userservice-dev.yaml[spring.application.name].yaml例如userservice.yaml 而[spring.application.name].yaml不包含环境因此可以被多个环境共享。 下面我们通过案例来测试配置共享 1添加一个环境共享配置 我们在 nacos 中添加一个 userservice.yaml 文件 2在user-service中读取共享配置 在 user-service 服务中修改 PatternProperties 类读取新添加的属性 在 user-service 服务中修改 UserController添加一个方法 3运行两个 UserApplication使用不同的 profile 修改 UserApplication2 这个启动项改变其 profile 值 这样UserApplication(8081) 使用的 profile 是 devUserApplication2(8082) 使用的 profile 是 test。 启动UserApplication和UserApplication2 访问 http://localhost:8081/user/prop结果 访问 http://localhost:8082/user/prop结果 可以看出来不管是 dev还是 test 环境都读取到了 envSharedValue 这个属性的值。 4配置共享的优先级 当 nacos、服务本地同时出现相同属性时优先级有高低之分 1.4.搭建 Nacos 集群 Nacos 生产环境下一定要部署为集群状态部署方式参考课前资料中的文档Nacos集群搭建.md 集群搭建步骤 搭建 MySQL 集群并初始化数据库表下载解压 nacos修改集群配置节点信息、数据库配置分别启动多个 nacos 节点nginx 反向代理 2.Feign 远程调用 先来看我们以前利用 RestTemplate 发起远程调用的代码 存在下面的问题 代码可读性差编程体验不统一参数复杂URL难以维护 Feign 是一个声明式的 http 客户端 官方地址: https://github.com/OpenFeign/feign 其作用就是帮助我们优雅的实现 http 请求的发送解决上面提到的问题。 2.1.Feign 替代 RestTemplate Fegin 的使用步骤如下 1引入依赖 我们在 order-service 服务的 pom 文件中引入 feign 的依赖 dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId
/dependency2添加注解 在 order-service 的启动类添加注解开启 Feign 的功能 3编写 Feign 的客户端 在 order-service 中新建一个接口内容如下 package cn.itcast.order.client;import cn.itcast.order.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;FeignClient(userservice)
public interface UserClient {GetMapping(/user/{id})User findById(PathVariable(id) Long id);
}这个客户端主要是基于 SpringMVC 的注解来声明远程调用的信息比如 服务名称userservice请求方式GET请求路径/user/{id}请求参数Long id返回值类型User 这样Feign 就可以帮助我们发送 http 请求无需自己使用 RestTemplate 来发送了。 4测试 修改 order-service 中的 OrderService 类中的 queryOrderById 方法使用 Feign 客户端代替 RestTemplate 5总结 使用 Feign 的步骤 引入依赖添加 EnableFeignClients 注解编写 FeignClient 接口使用 FeignClient 中定义的方法代替 RestTemplate 2.2.自定义配置 Feign可以支持很多的自定义配置如下表所示 类型作用说明feign.Logger.Level修改日志级别包含四种不同的级别NONE、BASIC、HEADERS、FULLfeign.codec.Decoder响应结果的解析器http远程调用的结果做解析例如解析json字符串为java对象feign.codec.Encoder请求参数编码将请求参数编码便于通过http请求发送feign. Contract支持的注解格式默认是SpringMVC的注解feign. Retryer失败重试机制请求失败的重试机制默认是没有不过会使用Ribbon的重试 一般情况下默认值就能满足我们使用如果要自定义时只需要创建自定义的 Bean 覆盖默认 Bean 即可。 下面以日志为例来演示如何自定义配置。 2.2.1.配置文件方式 基于配置文件修改 feign 的日志级别可以针对单个服务 feign: client:config: userservice: # 针对某个微服务的配置loggerLevel: FULL # 日志级别 也可以针对所有服务 feign: client:config: default: # 这里用default就是全局配置如果是写服务名称则是针对某个微服务的配置loggerLevel: FULL # 日志级别 而日志的级别分为四种 NONE不记录任何日志信息这是默认值。BASIC仅记录请求的方法URL以及响应状态码和执行时间HEADERS在BASIC的基础上额外记录了请求和响应的头信息FULL记录所有请求和响应的明细包括头信息、请求体、元数据。 2.2.2.Java 代码方式 也可以基于 Java 代码来修改日志级别先声明一个类然后声明一个 Logger.Level 的对象 public class DefaultFeignConfiguration {Beanpublic Logger.Level feignLogLevel(){return Logger.Level.BASIC; // 日志级别为BASIC}
}如果要全局生效将其放到启动类的 EnableFeignClients 这个注解中 EnableFeignClients(defaultConfiguration DefaultFeignConfiguration .class) 如果是局部生效则把它放到对应的 FeignClient 这个注解中 FeignClient(value userservice, configuration DefaultFeignConfiguration .class) 2.3.Feign 使用优化 Feign 底层发起 http 请求依赖于其它的框架。其底层客户端实现包括 URLConnection默认实现不支持连接池Apache HttpClient 支持连接池OKHttp支持连接池 因此提高 Feign 的性能主要手段就是使用 连接池 代替默认的 URLConnection。 这里我们用 Apache 的 HttpClient 来演示。 1引入依赖 在 order-service 的 pom 文件中引入 Apache 的 HttpClient 依赖 !--httpClient的依赖 --
dependencygroupIdio.github.openfeign/groupIdartifactIdfeign-httpclient/artifactId
/dependency2配置连接池 在 order-service 的 application.yml 中添加配置 feign:client:config:default: # default全局的配置loggerLevel: BASIC # 日志级别BASIC就是基本的请求和响应信息httpclient:enabled: true # 开启feign对HttpClient的支持max-connections: 200 # 最大的连接数max-connections-per-route: 50 # 每个路径的最大连接数总结Feign 的优化 日志级别尽量用 basic使用 HttpClient 或 OKHttp 代替 URLConnection 2.4.最佳实践 所谓最近实践就是使用过程中总结的经验最好的一种使用方式。 观察可以发现Feign 的客户端与服务提供者的 controller 代码非常相似 2.4.1.继承方式 一样的代码可以通过继承来共享 定义一个 API 接口利用定义方法并基于 SpringMVC 注解做声明。Feign 客户端和 Controller 都集成改接口 优点 简单实现了代码共享 缺点 服务提供方、服务消费方紧耦合参数列表中的注解映射并不会继承因此Controller中必须再次声明方法、参数列表、注解 2.4.2.抽取方式 将 Feign 的 Client 抽取为独立模块并且把接口有关的 POJO、默认的 Feign 配置都放到这个模块中提供给所有消费者使用。 例如将 UserClient、User、Feign 的默认配置都抽取到一个 feign-api 包中所有微服务引用该依赖包即可直接使用。 2.4.3.实现基于抽取的最佳实践
1抽取 首先创建一个 module命名为 feign-api 项目结构 在 feign-api 中然后引入 feign 的 starter 依赖 dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId
/dependency然后order-service 中编写的 UserClient、User、DefaultFeignConfiguration 都复制到 feign-api 项目中 2在 order-service 中使用 feign-api 首先删除 order-service 中的 UserClient、User、DefaultFeignConfiguration 等类或接口。 在 order-service 的 pom 文件中中引入 feign-api 的依赖 dependencygroupIdcn.itcast.demo/groupIdartifactIdfeign-api/artifactIdversion1.0/version
/dependency修改 order-service 中的所有与上述三个组件有关的导包部分改成导入 feign-api 中的包 3重启测试 重启后发现服务报错了 这是因为 UserClient 现在在 cn.itcast.feign.clients 包下 而 order-service 的 EnableFeignClients 注解是在 cn.itcast.order 包下不在同一个包无法扫描到 UserClient。 4解决扫描包问题 方式一指定 Feign 应该扫描的包 EnableFeignClients(basePackages cn.itcast.feign.clients)方式二指定需要加载的Client接口 EnableFeignClients(clients {UserClient.class})3.Gateway 服务网关 Spring Cloud Gateway 是 Spring Cloud 的一个全新项目该项目是基于 Spring 5.0Spring Boot 2.0 和 Project Reactor 等响应式编程和事件流技术开发的网关它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。 官网地址: spring-cloud-gateway 3.1.为什么需要网关 Gateway 网关是我们服务的守门神所有微服务的统一入口。 网关功能 身份认证和权限校验服务路由、负载均衡请求限流 架构图 权限控制网关作为微服务入口需要校验用户是是否有请求资格如果没有则进行拦截。路由和负载均衡一切请求都必须先经过 gateway但网关不处理业务而是根据某种规则把请求转发到某个微服务这个过程叫做路由。当然路由的目标服务有多个时还需要做负载均衡。限流当请求流量过高时在网关中按照下流的微服务能够接受的速度来放行请求避免服务压力过大。 在 SpringCloud 中网关的实现包括两种 gatewayzuul Zuul 是基于 Servlet 的实现属于阻塞式编程。而 SpringCloudGateway 则是基于 Spring5 中提供的 WebFlux属于响应式编程的实现具备更好的性能。 3.2.gateway 快速入门 下面我们就演示下网关的基本路由功能。基本步骤如下 创建 SpringBoot 工程 gateway引入网关依赖编写启动类编写基础配置和路由规则启动网关服务进行测试 1创建gateway服务引入依赖 创建服务 引入依赖
!--网关--
dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-gateway/artifactId
/dependency
!--nacos服务发现依赖--
dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId
/dependency2编写启动类
package cn.itcast.gateway;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication
public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}
}3编写基础配置和路由规则 创建 application.yml 文件内容如下 server:port: 10010 # 网关端口
spring:application:name: gateway # 服务名称cloud:nacos:server-addr: localhost:8848 # nacos地址gateway:routes: # 网关路由配置- id: user-service # 路由id自定义只要唯一即可# uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址uri: lb://userservice # 路由的目标地址 lb就是负载均衡后面跟服务名称predicates: # 路由断言也就是判断请求是否符合路由规则的条件- Path/user/** # 这个是按照路径匹配只要以/user/开头就符合要求我们将符合Path 规则的一切请求都代理到 uri参数指定的地址。 本例中我们将 /user/**开头的请求代理到lb://userservicelb是负载均衡根据服务名拉取服务列表实现负载均衡。 4重启测试 重启网关访问 http://localhost:10010/user/1 时符合/user/**规则请求转发到urihttp://userservice/user/1得到了结果 5网关路由的流程图 整个访问的流程如下 总结 网关搭建步骤 创建项目引入 nacos 服务发现和 gateway 依赖配置 application.yml包括服务基本信息、nacos 地址、路由 路由配置包括 路由id路由的唯一标示路由目标uri路由的目标地址http 代表固定地址lb 代表根据服务名负载均衡路由断言predicates判断路由的规则路由过滤器filters对请求或响应做处理 3.3.断言工厂 我们在配置文件中写的断言规则只是字符串这些字符串会被 Predicate Factory 读取并处理转变为路由判断的条件 例如 Path/user/** 是按照路径匹配这个规则是由 org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory类来处理的像这样的断言工厂在SpringCloudGateway还有十几个: 名称说明示例After是某个时间点后的请求- After2037-01-20T17:42:47.789-07:00[America/Denver]Before是某个时间点之前的请求- Before2031-04-13T15:14:47.43308:00[Asia/Shanghai]Between是某两个时间点之前的请求- Between2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver]Cookie请求必须包含某些cookie- Cookiechocolate, ch.pHeader请求必须包含某些header- HeaderX-Request-Id, \dHost请求必须是访问某个host域名- Host.somehost.org,.anotherhost.orgMethod请求方式必须是指定方式- MethodGET,POSTPath请求路径必须符合指定规则- Path/red/{segment},/blue/**Query请求参数必须包含指定参数- Queryname, Jack或者- QuerynameRemoteAddr请求者的ip必须是指定范围- RemoteAddr192.168.1.1/24Weight权重处理 我们只需要掌握 Path 这种路由工程就可以了。 3.4.过滤器工厂 GatewayFilter 是网关中提供的一种过滤器可以对进入网关的请求和微服务返回的响应做处理 3.4.1.路由过滤器的种类 Spring 提供了 31 种不同的路由过滤器工厂。例如 名称说明AddRequestHeader给当前请求添加一个请求头RemoveRequestHeader移除请求中的一个请求头AddResponseHeader给响应结果中添加一个响应头RemoveResponseHeader从响应结果中移除有一个响应头RequestRateLimiter限制请求的流量
3.4.2.请求头过滤器 下面我们以 AddRequestHeader 为例来讲解。 需求给所有进入 userservice 的请求添加一个请求头Truthitcast is freaking awesome! 只需要修改 gateway 服务的 application.yml文件添加路由过滤即可 spring:cloud:gateway:routes:- id: user-service uri: lb://userservice predicates: - Path/user/** filters: # 过滤器- AddRequestHeaderTruth, Itcast is freaking awesome! # 添加请求头GetMapping(/{id})public User queryById(PathVariable(id) Long id,RequestHeader(value Truth, required false) String truth) {System.out.println(truth: truth);return userService.queryById(id);}当前过滤器写在 userservice 路由下因此仅仅对访问 userservice 的请求有效。 3.4.3.默认过滤器 如果要对所有的路由都生效则可以将过滤器工厂写到 default下。格式如下 spring:cloud:gateway:routes:- id: user-service uri: lb://userservice predicates: - Path/user/**default-filters: # 默认过滤项- AddRequestHeaderTruth, Itcast is freaking awesome! 3.4.4.总结 过滤器的作用是什么 对路由的请求或响应做加工处理比如添加请求头配置在路由下的过滤器只对当前路由的请求生效 defaultFilters 的作用是什么 对所有路由都生效的过滤器 3.5.全局过滤器
3.5.1.全局过滤器作用 全局过滤器的作用也是处理一切进入网关的请求和微服务响应与 GatewayFilter 的作用一样。区别在于 GatewayFilter 通过配置定义处理逻辑是固定的而 GlobalFilter 的逻辑需要自己写代码实现。 定义方式是实现 GlobalFilter 接口。 public interface GlobalFilter {/*** 处理当前请求有必要的话通过{link GatewayFilterChain}将请求交给下一个过滤器处理** param exchange 请求上下文里面可以获取Request、Response等信息* param chain 用来把请求委托给下一个过滤器 * return {code MonoVoid} 返回标示当前过滤器业务结束*/MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain);
}在 filter 中编写自定义逻辑可以实现下列功能 登录状态判断权限校验请求限流等 3.5.2.自定义全局过滤器 需求定义全局过滤器拦截请求判断请求的参数是否满足下面条件 参数中是否有 authorizationauthorization 参数值是否为 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 {Overridepublic MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1.获取请求参数MultiValueMapString, 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();}
}3.5.3.过滤器执行顺序 请求进入网关会碰到三类过滤器当前路由的过滤器、DefaultFilter、GlobalFilter 请求路由后会将当前路由过滤器和 DefaultFilter、GlobalFilter合并到一个过滤器链集合中排序后依次执行每个过滤器 过滤器执行顺序 每一个过滤器都必须指定一个 int 类型的 order 值order 值越小优先级越高执行顺序越靠前。GlobalFilter 通过实现 Ordered 接口或者添加 Order 注解来指定 order 值由我们自己指定路由过滤器和 defaultFilter 的 order 由 Spring 指定默认是按照声明顺序从 1 递增。当过滤器的 order 值一样时会按照 defaultFilter 路由过滤器 GlobalFilter 的顺序执行。 详细内容可以查看源码 org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters()方法是先加载defaultFilters然后再加载某个route的filters然后合并。org.springframework.cloud.gateway.handler.FilteringWebHandler#handle()方法会加载全局过滤器与前面的过滤器合并后根据order排序组织过滤器链 3.6.跨域问题
3.6.1.什么是跨域问题 跨域域名不一致就是跨域主要包括 域名不同 www.taobao.com 和 www.taobao.org 和 www.jd.com 和 miaosha.jd.com域名相同端口不同localhost:8080和localhost8081 跨域问题浏览器禁止请求的发起者与服务端发生跨域 ajax 请求请求被浏览器拦截的问题解决方案CORS这个以前应该学习过这里不再赘述了。不知道的小伙伴可以查看 https://www.ruanyifeng.com/blog/2016/04/cors.html 3.6.2.模拟跨域问题 找到课前资料的页面文件 放入 tomcat 或者 nginx 这样的 web 服务器中启动并访问。 可以在浏览器控制台看到下面的错误 从 localhost:8090 访问 localhost:10010端口不同显然是跨域的请求。 3.6.3.解决跨域问题 在 gateway 服务的 application.yml 文件中添加下面的配置 spring:cloud:gateway:# 。。。globalcors: # 全局的跨域处理add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题corsConfigurations:[/**]:allowedOrigins: # 允许哪些网站的跨域请求 - http://localhost:8090allowedMethods: # 允许的跨域ajax的请求方式- GET- POST- DELETE- PUT- OPTIONSallowedHeaders: * # 允许在请求中携带的头信息allowCredentials: true # 是否允许携带cookiemaxAge: 360000 # 这次跨域检测的有效期