SpringCloud之微服务实用篇2
Posted nuist__NJUPT
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud之微服务实用篇2相关的知识,希望对你有一定的参考价值。
在之前我们学习微服务中的两个组件,一个是注册中心,一个负载均衡器。今天,我们主要学习三个内容,分别是:Nacos配置管理、Feign远程调用、Gateway服务网关。
目录
一、Nacos配置管理
1.1、Nacos实现配置管理
Nacos既可以作为注册中心,也可以用来作配置管理,微服务可以从nacos中进行注册发现和胚珠读取,具体如下所示:
在nacos的配置栏中点击➕添加配置,在弹出的列表中进行配置即可,并不是所有的都要配置,需要nacos统一管理的进行配置即可。
1.2、微服务配置拉取
因为现在需要读取nacos配置文件,故需要知道nacos的地址,所以需要在bootstrap文件中配置nacos地址信息,如下所示。
需要知道去哪里拉取配置信息,并且需要知道配置信息的名称,故需要先导入配置坐标,然后在bootstrap.yml文件中输入配置的名称。
最后在表现层,拉去配置信息,并通过浏览器请求并显示配置信息,如下:
//注入nacos中的配置属性
@Value("$pattern.dateformat")
private String dateFormat ;
@GetMapping("/now")
public String now()
System.out.println(dateFormat);
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateFormat, Locale.CHINA)) ;
从nacos拉取的配置信息,输出效果如下:
1.3、 配置热更新
Nacos配置更新后,微服务不需要重启就能自动感知,需要通过下面两种方式实现。
方式1:在@Value注解上注入变量所在的类上添加@RefreshScope注解。
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.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
@Slf4j
@RestController
@RequestMapping("/user")
@RefreshScope
public class UserController
@Autowired
private UserService userService;
//注入nacos中的配置属性
@Value("$pattern.dateformat")
private String dateFormat ;
@GetMapping("/now")
public String now()
System.out.println(dateFormat);
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateFormat, Locale.CHINA)) ;
@GetMapping("/id")
public User queryById(@PathVariable("id") Long id)
return userService.queryById(id);
方式2:使用@ConfigurationProperties注解,可以自动刷新。
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties
private String dateformat;
// private String envSharedValue;
// private String name;
在表现层将改配置作为bean注入进来,就可以使用了,如下:
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.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
@Slf4j
@RestController
@RequestMapping("/user")
@RefreshScope
public class UserController
@Autowired
private UserService userService;
//注入nacos中的配置属性
// @Value("$pattern.dateformat")
// private String dateFormat ;
@Autowired
private PatternProperties patternProperties ;
@GetMapping("/now")
public String now()
System.out.println(patternProperties.getDateformat());
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat(), Locale.CHINA)) ;
@GetMapping("/id")
public User queryById(@PathVariable("id") Long id)
return userService.queryById(id);
1.4、多环境配置共享
微服务启动时会从nacos读取多个配置文件,无论怎么变都会读取多环境的共享配置文件,所以共享配置环境的公共配置。
在nacos中创建两个配置,一个是dev环境下的,另外一个是多环境共享的。
那么便可以在程序中加载配置中的nacos共享配置以便使用。
那么需要考虑的是,如果本地配置贺远程配置中用相同的配置,系统会选择哪一个配置呢?这就需要考虑配置优先级的问题。
优先级:远程带环境的配置>远程不带环境的配置>本地配置
1.5、nacos集群搭建
我们假设有三台nacos节点,那么首先要实现的就是数据共享,我们实现mysql集群,让nacos来访问mysql集群,进而实现数据共享。为了将多个服务按照负载均衡分发到不同的nacos节点,故使用nginx实现负载均衡。
搭建nacos集群的基本步骤如下:
1)搭建数据库,初始化数据库表结构;2)下载nacos安装包;3)配置nacos;4)启动nacos集群;5)ngnix反向代理
二、Feign远程调用
2.1、基于Feign远程调用
首先我们看一下之前学的基于RestTemplate方式实现的服务的远程调用,该方法的代码可读性较差,参数复杂URL难以维护。
今天主要需要一个声明式的http客户端,以优雅的方式发送http请求。
首先需要在pom.xml加入坐标依赖;然后加入启动注解@EnableFeignClients;然后定义一个接口;最后使用接口中定义的方法替代RestTemplate实现远程调用。
<!--添加feign依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
@EnableFeignClients
public class OrderApplication
public static void main(String[] args)
SpringApplication.run(OrderApplication.class, args);
@LoadBalanced
@Bean //创建RestTemplate对象,并注入Spring容器
public RestTemplate restTemplate()
return new RestTemplate() ;
import cn.itcast.feign.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient("userservice")
public interface UserClient
@GetMapping("/user/id")
User getUserById(@PathVariable Long id) ;
最后使用接口的getUserById方法替代RestTeplate完成远程调用。
import cn.itcast.feign.pojo.User;
import cn.itcast.order.clients.UserClient;
import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderService
@Autowired
private OrderMapper orderMapper;
@Autowired
private UserClient userClient ;
public Order queryOrderById(Long orderId)
// 1.查询订单
Order order = orderMapper.findById(orderId);
//用feign实现远程调用
User user = userClient.getUserById(order.getUserId()) ;
//封装到order中
order.setUser(user);
// 4.返回
return order;
/*@Autowired
private RestTemplate restTemplate;
public Order queryOrderById(Long orderId)
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.利用RestTemplate发起http请求,查询用户
// 2.1.url路径
String url = "http://userservice/user/" + order.getUserId();
// 2.2.发送http请求,实现远程调用
User user = restTemplate.getForObject(url, User.class);
// 3.封装user到Order
order.setUser(user);
// 4.返回
return order;
*/
2.2、Feign自定义配置
在Feign中一般会通过自定义的配置去覆盖默认的配置,通过可以进行修改的配置如下所示。
自定义配置feign 的方式有两种,一种是配置文件方式进行配置,具体如下:
第二种方式是使用java代码自定义feign配置文件,可以配置为全局配置,也可以为局部配置。
2.3、Feign性能优化
Feign的性能优化主要包含两个方面,第一:使用连接池代替默认的URLConnection,日志级别最好为none。
Feign性能优化:连接池优化,主要就是引入依赖,然后在连接池配置参数。
2.4、Feign最佳实践分析与实现
我们先来看feign的最佳实践分析的第一种方式,如下:但是一般情况下,不推荐这种方法,会导致紧耦合问题。
第二种最佳实践方式,是采用抽取的方式,不同服务直接从api中引入依赖即可,最后在表现层实现服务的远程调用。
下面我们演示一下上述的最佳实践的方式2,主要分为5步,首先需要创建一个名称为feign-api的module,然后引入feign的依赖,然后在将需要抽取的内容复制到feign-api中,最后直接在服务中引入feign-api依赖就可以,当然相应的包也需要换成feign-api中的包,最后重启服务,测试即可。
这种方法在最后注入UserClient的时候发现不能注入,因为没有扫描到包,应该在@EnableFeignClients注解中加入要扫描的包或者直接指定要扫描的类,我直接扫描类,具体如下:
@EnableFeignClients(clients = UserClient.class)
三、Gateway服务网关
3.1、网关的作用及快速入门
网关的主要功能有三个,分别为:身份认证和权限校验;服务路由与负载均衡;请求限流等。
网关技术的两种实现方式如下,一般我们使用满足响应式编程的Gateway实现。
下面我们使用gateway进行网关搭建,具体步骤如下:
第一,创建一个module作为网关,在pom.xml文件中引入nacos服务发现依赖贺网关依赖。需要编写该服务的启动类,则网关的搭建就完成了。
第二步,需要编写路由以及配置nacos地址,需要定义路由的id,路由的目标地址以及路由的断言。其中lb为loadbalance表示负载均衡。
我们看一下上面通过网关进行路由的过程,首先是用户通过浏览器向网关发起请求,然后通过网关的配置进行路由规则判断,从nacos注册中心拉取相应的服务,然后根据负载均衡原理,选取相应的服务处理请求。
3.2、路由断言工厂
其实路由的配置主要包括以下四个部分,路由的唯一标识id,路由的目的地,路由断言,路由过滤器,这里主要学习路由断言部分的路由断言工厂。
断言工厂就是处理断言的规则,具体的断言工厂有很多种。
我们看一下常见的断言工厂,一共11个断言规则,根据相应的断言规则进行配置即可。
3.3、路由过滤器的配置
路由过滤器是网关中提供的一种过滤器,主要用来都对请求的信息和微服务响应的信息进行处理。
一般通过名称就可以判断出过滤器的名称,根据自己的需求设置过滤器就可以了。
直接在网关中的.yaml文件中进行路由配置即可,我配置了一个 默认的过滤器,对于所有路由都进行过滤,如下:主要看最后一行的配置就可以 ,最后一行是
server:
port: 10010
logging:
#level:
# cn.itcast: debug
#pattern:
# dateformat: MM-dd HH:mm:ss:SSS
spring:
application:
name: gateway
cloud:
nacos:
server-addr: localhost:8848 # nacos地址
gateway:
routes:
- id: user-service # 路由标示,必须唯一
uri: lb://userservice # 路由的目标地址
predicates: # 路由断言,判断请求是否符合规则
- Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是则符合
- id: order-service
uri: lb://orderservice
predicates:
- Path=/order/**
default-filters:
- AddRequestHeader=Truth,Itcast is freaking awesome!
3.4、全局过滤器
我们先了解一下什么是全局过滤器,全局过滤器和网关的过滤作用一样,都是对请求和响应进行处理的,但是全局过滤器可以自己写代码设置处理逻辑,即处理逻辑是不固定的。
下面编写一个全局过滤器实现相应的接口,在重写的方法中定义处理逻辑。
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
// @Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered
@Override
public 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();
@Override
public int getOrder()
return -1;
最后在浏览器测试,必须要加上设置的参数才能通过网关访问服务。
3.5、过滤器链的执行顺序
我们之前学过三种过滤器,那么这三种过滤器的执行 顺序如何呢,怎么进行排序呢?
过滤器的执行顺序的逻辑如下,对于order值来说,越小执行顺序越靠前,当order值一样时,会按照默认过滤器->路由过滤器->全局过滤器的顺序执行。
3.6、网关的cors跨域配置
我们先回顾一下跨域问题,跨域问题就是浏览器禁止发起者与服务端产生ajax请求,请求被浏览器拦截的问题,一般来说使用CORS跨域解决跨域问题。
网关处理跨域问题也是CORS方案,并且只需要简单的如下配置即可实现。一般要配置的参数如下所示。
以上是关于SpringCloud之微服务实用篇2的主要内容,如果未能解决你的问题,请参考以下文章