SpringCloud基础学习
SpringCloud
private RestTemplate restTemplate;//提供多种边界访问远程http服务的方法,简单的Restful服务模板 //(url,实体:Map,Class<T> responseType) @Autowired private RestTemplate restemplate; private static final String REST_URL_PREFIX = "http://localhost:8081"; @RequestMapping("/consumer/dept/add") public boolean add(Dept dept){ return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class); }
1.Ribbon
Ribbon负载均衡
负载均衡策略,通过定义IRule可以修改负载均衡规则,有两种方式
-
代码方式:在order-service中的OrderApplication类中,定义一个新的IRule:
@Bean public IRule randomRule(){ return new RandomRule(); } //针对全局
-
配置文件方式:在order-service的application.yml文件中,添加新的配置也能修改规则
userservice: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule //针对某个微服务
Ribbon饥饿加载
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启
ribbon: eager-load: enabled: true # 开启饥饿加载 clients: userservice # 指定对userservice这个服务饿加载
2.Nacos
注册中心
服务注册
-
启动微服务
-
引入服务发现依赖
-
配置Nacos地址
-
查看注册中心效果
-
集群模式启动测试
服务集群属性
spring: cloud: nacos: server-addr: localhost:8848 # nacos服务端地址 discovery: cluster-name: HZ # 配置集群名称,也就是机房位置
服务发现
-
开启服务发现功能 @EnableDiscoveryClient
-
测试服务发现AI
@SpringBootTest public class DiscoveryTest { @Autowired DiscoveryClient discoveryClient; @Test void discoveryClientTest(){ for (String service : discoveryClient.getServices()){ System.out.println("service = " + service); //获取ip+端口 List<ServiceInstance> instances = discoveryClient.getInstances(service); for (ServiceInstance instance : instances) { System.out.println("ip:"+instance.getHost()+";"+"port = "+ instance.getPort()); } } } }
负载均衡
-
引入负载均衡依赖 spring-cloud-starter-localbalancer
-
测试负载均衡API LoadBalancerClient
-
测试远程调用 RestTemplate
-
测试负载均衡调用 @LoadBalanced
// 完成负载均衡的发送请求 private Product getProductFromRemoteWithLoadBalance(Long productId){ //1.获取到商品服务所在的所有机型IP+port ServiceInstance choose = loadBalancerClient.choose("service-product"); //远程URL String url = "http://"+choose.getHost()+":"+choose.getPort()+"/product/"+productId; log.info("远程请求:{}",url); //2.给远程发送请求 Product product = restTemplate.getForObject(url,Product.class); return product; }
也可以将@LoadBalanced写到远程调用客户端上,也可以实现负载均衡
// 基于注解负载均衡的发送请求 private Product getProductFromRemoteWithLoadBalance(Long productId){ String url = "http://service-product/product/"+productId; //2.给远程发送请求 service-product会被动态替换 Product product = restTemplate.getForObject(url,Product.class); return product; }
如果注册中心宕机,远程调用还能成功嘛 两种情况,是否调用过
服务实例的权重设置
-
在Nacos控制台可以设置实例的权重值,首先选中实例后面的编辑按钮
-
将权重设置为0.1,测试可以发现8081被访问到的频率大大降低
环境隔离
Nacos中服务存储 和数据存储的最外层都是一个名为namespace的东西,用来做最外层隔离
修改order-service的application.yml,添加namespace:
spring: datasource: url: jdbc:mysql://localhost:3306/zkw?useSSL=false namespace: root password: 123456 driver-class-name: com.mysql.jdbc.Driver cloud: nacos: server-addr: localhost:8848 discovery: cluster-name: SH # 上海 namespace: # 命名空间
配置中心
设置 配置文件的id:[服务名称]-[profile].[后缀名]
基本使用
-
启动Nacos
-
引入依赖
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
-
在userservice中的resource目录添加一个bootstrap.yml文件,这个文件是引导文件
spring: application: name: userservice # 服务名称 profiles: active: dev # 开发环境 cloud: nacos: server-addr: localhost:8848 # Nacos地址 config: file-extension: yaml # 文件后缀名
-
创建data-id(数据集)
动态刷新
-
@Value("${xx}")获取配置 + @RefreshScope 实现自动刷新
-
@ConfigurationProperties 无感自动刷新
-
NacosConfigManager 监听配置变化
Nacos中的数据集和application.properties有相同的配置,哪个会生效?
先导入优先,外部优先
nacos集群搭建
3.Feign
Feign是一个声明式的http客户端
定义和使用Feign客户端
-
引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <dependency>
-
在order-service的启动类添加注解并开启Feign的功能
@EnableFeiClients @MapperScan("cn.itcast.order.mapper") @SpringBootApplication public class OrderApplication { public static void main(String[] args){ SpringApplication.run(OrderApplication.class, args); } }
-
编写Feign客户端
@FeignClient("userservice") public interface UserClient { @GetMapping("/user/{id}") User findById(@PathVariable("id") Long id); }
主要是基于SpringMVC的注解来声明远程调用信息
Feign自定义配置
配置Feign日志有两种方式:
方式一:配置文件方式
-
全局生效:
feign: client: config: default: #这里用default就是全局配置,如果是写服务器名称,则是针对某个微服务的配置 loggerLevel: FULL #日志级别
-
局部生效
feign: client: config: userservice: loggerLevel: FULL
方式二:java代码方式,需要先声明一个Bean
public class FeignClientConfiguration{ @Bean public Logger.Level feignLogLevel(){ return Logger.level.BASIC; } }
-
全局配置:则把它放到@EnableFeignClients这个注解中
@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)
-
局部配置:则把它反到@FeignClient这个注解中
@FeignClients(value = "userservice",configuration = FeignClientConfiguration.class)
Feign性能优化
Feign底层实现:
-
URLConnection:默认实现,不支持连接池
-
Apache HttpClien:支持连接池
-
OKHttp:支持连接池
因此性能优化主要包括:
-
使用连接池代替默认的URLConnection
-
日志级别,最好用basic或none
性能优化-连接池配置
1.引入依赖
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>
2.配置连接池
feign: client: config: default: loggerLevel: BASIC httpclient: enable: true max-connections: 200 #最大连接数 max-connections-per-route:50 #mei'ge
Feign的最佳实践
方式一(继承):给消费者的FeignClient和提供者的controller定义统一的父接口作为标准
public interface UserAPI{ @GetMapping("/user/{id}") User findById(@PathVariable("id") Long id); } ---------------------------------------------- @FeignClient(value = "userservice") public interface UserClient extend UserAPI{ } ---------------------------------------------- @RestController public class UserController implements UserAPI{ }
方式二(抽取):将FeignClient抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用
-
首先创建一个module,命为feign-api,然后引入feign的starter依赖
-
将order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中
-
在order-service中引入feign-api的依赖
-
修改order-service中的所有与上述三个组件有关的import部分,改成导入feign-api中的包
-
重启测试
4.网关
网关功能:
-
身份认证和权限校验
-
服务路由、负载均衡
-
请求限流
搭建网关服务
-
创建新的module,引入SpringCloudGateway的依赖和nacos的服务发现依赖
<!--网关依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!--nacos服务发现依赖--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
-
编写路由配置及nacos地址
server: port: 10010 # 网管端口 spring: application: name: getway # 服务名称 cloud: nacos: server-addr: localhost:8848 # nacos地址 gateway: routes: # 网关路由配置 - id: user-service # 路由id,自定义,只要唯一即可 # uri: 路由的目标地址 http就是固定地址 uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称 predicates: # 路由断言,也就是判断请求是否符合路由规则的条件 - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
路由断言工厂
网关路由可以配置的内容包括:
-
路由id:路由唯一标识
-
uri:路由目的地,支持lb和http两种
-
predicates:路由断言,盘软请求是否符合要求,符合则转发到路由目的地
-
filters:路由过滤器,处理请求或响应
路由的过滤器配置
GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回 的响应做处理
给所有进入userservice的请求添加一个请求头
实现方式:在gateway中修改application.yml文件,给userservice的路由添加过滤器
spring: cloud: gateway: routes: #网关路由配置 - id: userservice uri: lb://userservice predicates: - Path=/user/** filters: #过滤器 - AddRequestHeader=Truth,Itcast is freaking awesome! #添加请求头
要对所有的路由都生效,则可以将过滤器工厂写道default下
spring: application: name: gateway #服务名称 cloud: nacos: server-addr: localhost:8848 #nacos地址 gateway: routes: #网关路由配置 - id: userservice uri: lb://userservice predicates: - Path=/user/** - id: order-service uri: lb://orderservice predicates: - Path=/order/** default-filters: #默认过滤器,会对所有的路由请求都生效 - AddRequestHeader=Truth,Itcast is freaking awesome!
全局过滤器 GlobalFilter
全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样定定义方式是实现GlobalFilter接口
@Order(-1) @Component public interface GlobalFilter { /** * 处理当前请求,有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器 * *@param exchange 请求上下文,里面可以获取Request、Response信息 *@param chain 用来把请求委托给下一个过滤器 *@return {@code Mono<void>} 返回标示当前过滤器业务结束 */ @Override Mono<Void> filter(ServerWebExchange exchange,GatewayFilterChain chain){ //1.获取请求参数 ServerHttpRequest request = exchange.getRequest(); MultiValueMap<String,String> params = request.getQueryParams(); //2.获取请求参数中的 authorization 参数 String auth = params.getFirst("authorization"); //3.判断参数值是否等于admin if("admin".equals(auth)){ //4.是,放行 return chain.filter(exchange); } //5.否,拦截 //5.1 设置状态码 exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); //5.2 拦截请求 return exchange.getResponse().setComplete(); } }
过滤器链执行顺序
每个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前;其中GlobalFilter由我们指定,路由过滤器和defaultFilter的order则是由Spring指定,默认是按照声明顺序从1开始
当过滤器的order值一样时 defaultFilter > 路由过滤器 > GlobalFilter
跨域问题处理
网关处理跨域采用的是CORS方案
spring: cloud: gateway: globalcors: #全局的跨域处理 add-to-simple-url-handler-mapping: true #解决options请求被拦截问题 corsConfigurations: '[/**]': allowedOrigins: #允许哪些网站的跨域请求 - "http://localhost:8090" - "http://www.leyou.com" allowedMethods: #允许的跨域ajax请求方式 - "GET" - "POST" - "DELETE" - "PUT" - "OPTIONS" allowedHeaders: "*" #允许在请求中携带的头信息 allowCredentials: true #是否允许携带cookie maxAge: 36000 #这次跨域检测的有效期