一、SpringCloud介绍
1.1 微服务架构
微服务架构的提出者:马丁福勒
简而言之,微服务架构样式[1]是一种将单个应用程序开发为一组小服务的方法,每个小服务都在自己的进程中运行并与轻量级机制(通常是HTTP资源API)进行通信。这些服务围绕业务功能构建,并且可以由全自动部署机制独立部署。这些服务的集中管理几乎没有,它可以用不同的编程语言编写并使用不同的数据存储技术。
1、 微服务架构只是一个样式,一个风格。
2、 将一个完整的项目,拆分成多个模块去分别开发。
3、 每一个模块都是单独的运行在自己的容器中。
4、 每一个模块都是需要相互通讯的。 Http,RPC,MQ。
5、 每一个模块之间是没有依赖关系的,单独的部署。
6、 可以使用多种语言去开发不同的模块。
7、 使用MySQL数据库,Redis,ES去存储数据,也可以使用多个MySQL数据库。
总结:将复杂臃肿的单体应用进行细粒度的划分,每个拆分出来的服务各自打包部署。
1.2 SpringCloud介绍
SpringCloud是微服务架构落地的一套技术栈。
SpringCloud中的大多数技术都是基于Netflix公司的技术进行二次研发。
SpringCloud的中文社区网站:http://springcloud.cn/
SpringCloud的中文网:Spring Cloud中文网-官方文档中文版
八个技术点:
Eureka - 服务的注册与发现
Ribbon - 服务之间的 负载均衡
Feign - 服务之间的通讯
Hystrix - 服务的线程隔离以及断路器
Zuul - 服务网关
Stream - 实现MQ的使用
Config - 动态配置
Sleuth - 服务追踪
二、服务的注册与发现-Eureka【重点
】
2.1 引言
Eureka就是帮助我们维护所有服务的信息,以便服务之间的相互调用
Eureka |
---|
2.2 Eureka的快速入门
2.2.1 创建EurekaServer
此案例中springboot版本为2.6.2,cloud版本为2021.0.0
创建一个父工程,并且在父工程中指定SpringCloud的版本,并且将packaging修改为pom
<packaging>pom</packaging> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2021.0.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
创建eureka的server,创建SpringBoot工程,并且导入依赖,在启动类中添加注解,编写yml文件
导入依赖
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
启动类添加注解
@SpringBootApplication @EnableEurekaServer public class EurekaApplication { public static void main(String[] args) { SpringApplication.run(EurekaApplication.class,args); } }
编写yml配置文件
server: port: 8761 # 端口号 eureka: instance: hostname: localhost # localhost client: # 当前的eureka服务是单机版的 registerWithEureka: false fetchRegistry: false serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
2.2.2 创建EurekaClient
创建Maven工程,修改为SpringBoot
导入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
在启动类上添加注解
@SpringBootApplication @EnableEurekaClient public class CustomerApplication { public static void main(String[] args) { SpringApplication.run(CustomerApplication.class,args); } }
编写配置文件
# 指定Eureka服务地址 eureka: client: service-url: defaultZone: http://localhost:8761/eureka #指定服务的名称 spring: application: name: CUSTOMER
2.2.3 测试Eureka
创建了一个Search搜索模块,并且注册到Eureka
使用到EurekaClient的对象去获取服务信息
@Autowired private EurekaClient eurekaClient;
正常RestTemplate调用即可
@GetMapping("/customer") public String customer(){ //1. 通过eurekaClient获取到SEARCH服务的信息 InstanceInfo info = eurekaClient.getNextServerFromEureka("SEARCH", false); //2. 获取到访问的地址 String url = info.getHomePageUrl(); System.out.println(url); //3. 通过restTemplate访问 //String result = restTemplate.getForObject(url + "/search", String.class); //4. 返回 return result; }
2.3 Eureka的安全性
实现Eureka认证
导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
编写配置类
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // 忽略掉/eureka/** http.csrf().ignoringAntMatchers("/eureka/**"); super.configure(http); } }
编写配置文件
# 指定用户名和密码 spring: security: user: name: root password: root
其他服务想注册到Eureka上需要添加用户名和密码
eureka: client: service-url: defaultZone: http://用户名:密码@localhost:8761/eureka
2.4 Eureka的高可用
如果程序的正在运行,突然Eureka宕机了。
如果调用方访问过一次被调用方了,Eureka的宕机不会影响到功能。
如果调用方没有访问过被调用方,Eureka的宕机就会造成当前功能不可用。
搭建Eureka高可用
准备多台Eureka
如果想在一台机器上配置多太eureka构建高可用集群,由于都使用localhost会失败,所以此处需要修改hosts文件。
打开系统的windows/system32/drivers/etc/文件夹,找到hosts文件,在最后添加如下内容:
127.0.0.1 server1127.0.0.1 server2
修改application.yml文件
spring: application: name: myeurekaserver security: user: name: root #设置注册中心的安全用户 password: root #设置注册中心的安全密码 server: port: 8761 eureka: instance: hostname: server1 client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://root:root@server2:8762/eureka/ server: enable-self-preservation: false #不开启在线保护机制(开发阶段不开启(设置为false))
然后启动当前服务器。
再次修改application.yml
spring: application: name: myeurekaserver security: user: name: root #设置注册中心的安全用户 password: root #设置注册中心的安全密码 server: port: 8762 eureka: instance: hostname: server2 client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://root:root@server1:8761/eureka/ server: enable-self-preservation: false #不开启在线保护机制(开发阶段不开启(设置为false))
再启动,如此,就启动了两台服务器,并互相建立了注册和发现。
让服务注册到多台Eureka
eureka: client: service-url: defaultZone: http://root:root@server1:8761/eureka,http://root:root@server2:8762/eureka
其实上面只需要让客户端注册到一台正常运行的服务器上,就会自动注册到其他服务器上。所以上面客户端的配置其实也可以只写一个服务端的地址,如下:
eureka: client: service-url: defaultZone: http://root:root@server1:8761/eureka
2.5 Eureka的细节
EurekaClient启动是,将自己的信息注册到EurekaServer上,EurekaSever就会存储上EurekaClient的注册信息。
当EurekaClient调用服务时,本地没有注册信息的缓存时,去EurekaServer中去获取注册信息。
EurekaClient会通过心跳的方式去和EurekaServer进行连接。(默认30sEurekaClient会发送一次心跳请求,如果超过了90s还没有发送心跳信息的话,EurekaServer就认为你宕机了,将当前EurekaClient从注册表中移除)
eureka: instance: lease-renewal-interval-in-seconds: 30 #心跳的间隔 lease-expiration-duration-in-seconds: 90 # 多久没发送,就认为你宕机了
EurekaClient会每隔30s去EurekaServer中去更新本地的注册表
eureka: client: registry-fetch-interval-seconds: 30 # 每隔多久去更新一下本地的注册表缓存信息
Eureka的自我保护机制,统计15分钟内,如果一个服务的心跳发送比例低于85%,EurekaServer就会开启自我保护机制
不会从EurekaServer中去移除长时间没有收到心跳的服务。
EurekaServer还是可以正常提供服务的。
网络比较稳定时,EurekaServer才会开始将自己的信息被其他节点同步过去
eureka: server: enable-self-preservation: true # 开启自我保护机制
CAP定理,C - 一致性,A-可用性,P-分区容错性,这三个特性在分布式环境下,只能满足2个,而且分区容错性在分布式环境下,是必须要满足的,只能在AC之间进行权衡。
如果选择CP,保证了一致性,可能会造成你系统在一定时间内是不可用的,如果你同步数据的时间比较长,造成的损失大。
BASE理论:基本可用,软状态,最终一致性。
Eureka就是一个AP的效果,高可用的集群,Eureka集群是无中心,Eureka即便宕机几个也不会影响系统的使用,不需要重新的去推举一个master,也会导致一定时间内数据是不一致。
三、负载均衡的客户端-Ribbon【重点
】
3.1 引言
Ribbon是帮助我们实现服务和服务负载均衡,ribbon属于客户端负载均衡
客户端负载均衡:customer客户模块,将2个Search模块信息全部拉取到本地的缓存,在customer中自己做一个负载均衡的策略,选中某一个服务。
服务端负载均衡:在注册中心中,直接根据你指定的负载均衡策略,帮你选中一个指定的服务信息,并返回。
Ribbon |
---|
3.2 Ribbon的快速入门
启动两个search模块
在customer中编写代码:
配置整合RestTemplate和Ribbon
@Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); }
在customer中去访问search
@GetMapping("/customer") public String customer(){ String result = restTemplate.getForObject("http://SEARCH/search", String.class); //4. 返回 return result; }
3.3 Ribbon配置负载均衡策略
负载均衡策略
RandomRule:随机策略
RoundribbonRule:轮询策略
WeightedResponseTimeRule:默认会采用轮询的策略,后续会根据服务的响应时间,自动给你分配权重
BestAvailableRule:根据被调用方并发数最小的去分配
四、声明式服务调用-Feign【重点
】
4.1 引言
Feign可以帮助我们实现面向接口编程,就直接调用其他的服务,简化开发。
4.2 Feign的快速入门
导入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
在Appliction类上添加一个注解
@EnableFeignClients
创建一个接口,并且和Search模块做映射
@FeignClient("SEARCH") // 指定服务名称 public interface SearchClient { // value -> 目标服务的请求路径,method -> 映射请求方式 @RequestMapping(value = "/search",method = RequestMethod.GET) String search(); }
测试使用
@Autowired private SearchClient searchClient; @GetMapping("/customer") public String customer(){ String result = searchClient.search(); return result; }
4.3 Feign的传递参数方式
【注意事项】
如果你传递的参数,比较复杂时,默认会采用POST的请求方式。
传递单个参数时,推荐使用@PathVariable,如果传递的单个参数比较多,这里也可以采用@RequestParam,不要省略value属性
传递对象信息时,统一采用json的方式,添加@RequestBody
在Search模块下准备三个接口
@GetMapping("/search/{id}") public Customer findById(@PathVariable Integer id){ return new Customer(1,"张三",23); } @GetMapping("/getCustomer") public Customer getCustomer(@RequestParam Integer id,@RequestParam String name){ return new Customer(id,name,23); } @PostMapping("/save") public Customer save(@RequestBody Customer customer){ return customer; }
封装Customer模块下的Controller
@GetMapping("/customer/{id}") public Customer findById(@PathVariable Integer id){ return searchClient.findById(id); } @GetMapping("/getCustomer") public Customer getCustomer(@RequestParam Integer id, @RequestParam String name){ return searchClient.getCustomer(id,name); } @GetMapping("/save") // 会自动转换为POST请求 405 public Customer save(Customer customer){ return searchClient.save(customer); }
再封装Client接口
@RequestMapping(value = "/search/{id}",method = RequestMethod.GET) Customer findById(@PathVariable(value = "id") Integer id); @RequestMapping(value = "/getCustomer",method = RequestMethod.GET) Customer getCustomer(@RequestParam(value = "id") Integer id, @RequestParam(value = "name") String name); @RequestMapping(value = "/save",method = RequestMethod.POST) Customer save(@RequestBody Customer customer);
测试
4.4 Feign的Fallback
Fallback可以帮助我们在使用Feign去调用另外一个服务时,如果出现了问题,走服务降级,返回一个错误数据,避免功能因为一个服务出现问题,全部失效。
注意:2020年以后的spring-cloud版本已经没有默认支持hystrix,需要手动导入依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.10.RELEASE</version> </dependency>
4.4.1 FallBack方式
创建一个POJO类,实现Client接口。
@Component public class SearchClientFallBack implements SearchClient { @Override public String search() { return "出现问题啦!!!"; } @Override public Customer findById(Integer id) { return null; } @Override public Customer getCustomer(Integer id, String name) { return null; } @Override public Customer save(Customer customer) { return null; } }
修改CLient接口中的注解,添加一个属性。
@FeignClient(value = "SEARCH",fallback = SearchClientFallBack.class)
添加一个配置文件。
feign: hystrix: enabled: true
4.4.1 FallBackFactory方式
调用方无法知道具体的错误信息是什么,通过FallBackFactory的方式去实现这个功能
FallBackFactory基于Fallback
创建一个POJO类,实现FallBackFactory<Client>
@Component public class SearchClientFallBackFactory implements FallbackFactory<SearchClient> { @Autowired private SearchClientFallBack searchClientFallBack; @Override public SearchClient create(Throwable throwable) { throwable.printStackTrace(); return searchClientFallBack; } }
修改Client接口中的属性
@FeignClient(value = "SEARCH",fallbackFactory = SearchClientFallBackFactory.class)
五、服务的隔离及断路器-Hystrix【重点
】
5.1 引言
Hystrix |
---|
5.2 降级机制实现
导入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.10.RELEASE</version> </dependency>
添加一个注解
@EnableCircuitBreaker
针对某一个接口去编写他的降级方法
@GetMapping("/customer/{id}") @HystrixCommand(fallbackMethod = "findByIdFallBack") public Customer findById(@PathVariable Integer id){ int i = 1/0; return searchClient.findById(id); } // findById的降级方法 方法的描述要和接口一致 public Customer findByIdFallBack(Integer id){ return new Customer(-1,"",0); }
在接口上添加注解
@HystrixCommand(fallbackMethod = "findByIdFallBack")
5、 测试一下
效果 |
---|
六、服务的网关-Zuul【重点
】
6.1 引言
客户端维护大量的ip和port信息,直接访问指定服务
认证和授权操作,需要在每一个模块中都添加认证和授权的操作
项目的迭代,服务要拆分,服务要合并,需要客户端进行大量的变化
统一的把安全性校验都放在Zuul中
zuul |
---|
6.2 Zuul的快速入门
创建Maven项目,修改为SpringBoot
导入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency>
添加一个注解
@EnableEurekaClient @EnableZuulProxy
编写配置文件
# 指定Eureka服务地址 eureka: client: service-url: defaultZone: http://root:root@localhost:8761/eureka,http://root:root@localhost:8762/eureka #指定服务的名称 spring: application: name: ZUUL server: port: 80
使用http://localhost/customer/customer发现无法直接测试,会出现异常,原因是新版本不支持ZUUL,需要自己添加配置,修复bug。添加配置类:
@Configuration public class ZuulConfiguration { /** * The path returned by ErrorController.getErrorPath() with Spring Boot < 2.5 * (and no longer available on Spring Boot >= 2.5). */ private static final String ERROR_PATH = "/error"; private static final String METHOD = "lookupHandler"; /** * Constructs a new bean post-processor for Zuul. * * @param routeLocator the route locator. * @param zuulController the Zuul controller. * @param errorController the error controller. * @return the new bean post-processor. */ @Bean public ZuulPostProcessor zuulPostProcessor(@Autowired RouteLocator routeLocator, @Autowired ZuulController zuulController, @Autowired(required = false) ErrorController errorController) { return new ZuulPostProcessor(routeLocator, zuulController, errorController); } private enum LookupHandlerCallbackFilter implements CallbackFilter { INSTANCE; @Override public int accept(Method method) { if (METHOD.equals(method.getName())) { return 0; } return 1; } } private enum LookupHandlerMethodInterceptor implements MethodInterceptor { INSTANCE; @Override public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { if (ERROR_PATH.equals(args[0])) { // by entering this branch we avoid the ZuulHandlerMapping.lookupHandler method to trigger the // NoSuchMethodError return null; } return methodProxy.invokeSuper(target, args); } } private static final class ZuulPostProcessor implements BeanPostProcessor { private final RouteLocator routeLocator; private final ZuulController zuulController; private final boolean hasErrorController; ZuulPostProcessor(RouteLocator routeLocator, ZuulController zuulController, ErrorController errorController) { this.routeLocator = routeLocator; this.zuulController = zuulController; this.hasErrorController = (errorController != null); } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (hasErrorController && (bean instanceof ZuulHandlerMapping)) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(ZuulHandlerMapping.class); enhancer.setCallbackFilter(LookupHandlerCallbackFilter.INSTANCE); // only for lookupHandler enhancer.setCallbacks(new Callback[] {LookupHandlerMethodInterceptor.INSTANCE, NoOp.INSTANCE}); Constructor<?> ctor = ZuulHandlerMapping.class.getConstructors()[0]; return enhancer.create(ctor.getParameterTypes(), new Object[] {routeLocator, zuulController}); } return bean; } } }
6.3 Zuul常用配置信息
6.3.1 Zuul的监控界面
导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
编写配置文件
# 查看zuul的监控界面(开发时,配置为*,上线,不要配置) management: endpoints: web: exposure: include: "*"
直接访问
6.3.2 忽略服务配置
# zuul的配置 zuul: # 基于服务名忽略服务,无法查看 ,如果要忽略全部的服务 "*",默认配置的全部路径都会被忽略掉(自定义服务的配置,无法忽略的) ignored-services: eureka # 监控界面依然可以查看,在访问的时候,404 ignored-patterns: /**/search/**
6.3.3 自定义服务配置
# zuul的配置 zuul: # 指定自定义服务(方式一 , key(服务名):value(路径)) # routes: # search: /ss/** # customer: /cc/** # 指定自定义服务(方式二) routes: cx: # 自定义名称 path: /cx/** # 映射的路径 serviceId: search # 服务名称
6.4 Zuul的过滤器执行流程
客户端请求发送到Zuul服务上,首先通过PreFilter链,如果正常放行,会吧请求再次转发给RoutingFilter,请求转发到一个指定的服务,在指定的服务响应一个结果之后,再次走一个PostFilter的过滤器链,最终再将响应信息交给客户端。
过滤器 |
---|
6.5 Zuul过滤器入门
创建POJO类,继承ZuulFilter抽象类
@Component public class TestZuulFilter extends ZuulFilter {}
指定当前过滤器的类型
@Override public String filterType() { return FilterConstants.PRE_TYPE; }
指定过滤器的执行顺序
@Override public int filterOrder() { return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1; }
配置是否启用
@Override public boolean shouldFilter() { // 开启当前过滤器 return true; }
指定过滤器中的具体业务代码
@Override public Object run() throws ZuulException { System.out.println("prefix过滤器执行~~~"); return null; }
测试
效果 |
---|
6.6 PreFilter实现token校验
准备访问路径,请求参数传递token
http://localhost/customer/version?token=123
创建AuthenticationFilter
@Component public class AuthenticationFilter extends ZuulFilter { @Override public String filterType() { return FilterConstants.PRE_TYPE; } @Override public int filterOrder() { return PRE_DECORATION_FILTER_ORDER - 2; } @Override public boolean shouldFilter() { return true; } @Override public Object run() throws ZuulException { //.. } }
在run方法中编写具体的业务逻辑代码
@Override public Object run() throws ZuulException { //1. 获取Request对象 RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletRequest request = requestContext.getRequest(); //2. 获取token参数 String token = request.getParameter("token"); //3. 对比token if(token == null || !"123".equalsIgnoreCase(token)) { //4. token校验失败,直接响应数据 requestContext.setSendZuulResponse(false); requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); } return null; }
测试
效果 |
---|
七、服务的动态配置-Config【重点
】
7.1 引言
配置文件分散在不同的项目中,不方便维护。
配置文件的安全问题。
修改完配置文件,无法立即生效。
config |
---|
7.2 搭建Config-Server
创建Maven工程,修改为SpringBoot
导入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
添加注解
@EnableConfigServer @EnableEurekaClient
编写配置文件(Git的操作)
spring: cloud: config: server: git: basedir: D:\basedir # 本地仓库的地址 username: nswdxqx1999wxf # 远程仓库用户名 password: nswdxqx1999 # 远程仓库密码 uri: https://gitee.com/migid/myconfig.git # 远程仓库地址 search-paths: configs
测试(http://localhost:port/{label}/{application}-{profile}.yml)
效果 |
---|
7.3 搭建Config-Client
导入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-client</artifactId> </dependency>
修改原来的search服务的配置文件
# 指定Eureka服务地址 eureka: client: service-url: defaultZone: http://root:root@localhost:8761/eureka spring: application: name: SEARCH cloud: config: profile: dev discovery: enabled: true serviceId: CONFIG-SERVER name: mycloudtest label: master # CONFIG -> mycloudtest-dev.yml
修改配置名称
修改为bootstrap.yml
修改SearchController.java
@Value("${test_name}") private String name; @GetMapping("/search") public String search(){ return "search:" + port + "-name:" + name; }
效果 |
---|
7.4 使用bus通过rabbitMQ通知刷新配置
修改search配置:
添加依赖:
<!-- 消息总线与rabbitMQ配置--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency> <!-- 消息传动配置 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
修改bootstrap.yml文件
spring: application: name: mysearch cloud: config: profile: dev discovery: enabled: true serviceId: CONFIGSERVER name: mycloudtest label: master bus: enabled: true trace: enabled: true rabbitmq: host: localhost username: guest password: guest port: 5672 server: port: 9101 eureka: client: service-url: defaultZone: http://root:root@localhost:8761/eureka/ # 通过用户名密码连接注册中心 instance: prefer-ip-address: true management: endpoints: web: exposure: include: "*" #释放所有的接口,主要释放 /actuator/bus-refresh 接口,该接口需要配置到 WebHooks 用于在提价的配置的时候通知ConfigServer
修改appliction文件
@EnableDiscoveryClient @EnableEurekaClient @SpringBootApplication public class MySearchApplication { public static void main(String[] args) { SpringApplication.run(MySearchApplication.class, args); } }
再修改SearchController.java,添加注解
@RefreshScope // 动态加载配置
测试效果:
1、打开浏览器:http://localhost:9101/search,查看原来的数据
2、修改git上配置文件内容
3、发送
post
请求:http://localhost:9002/actuator/bus-refresh4、再次查看http://localhost:9101/search数据,看是否动态刷新
八、服务的追踪-Sleuth【重点
】
8.1 引言
在整个微服务架构中,微服务很多,一个请求可能需要调用很多很多的服务,最终才能完成一个功能,如果说,整个功能出现了问题,在这么多的服务中,如何去定位到问题的所在点,出现问题的原因是什么。
Sleuth可以获得到整个服务链路的信息。
Zipkin通过图形化界面去看到信息。
Sleuth将日志信息存储到数据库中。
Sleuth |
---|
8.2 Sleuth的使用
导入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency>
编写配置文件
logging: level: org.springframework.web.servlet.DispatcherServlet: DEBUG
测试
日志信息 |
---|
SEARCH:服务名称 e9c:总链路id f07:当前服务的链路id false:不会将当前的日志信息,输出其他系统中
8.3 Zipkin的使用
搭建Zipkin的web工程 OpenZipkin · A distributed tracing system,官方推荐直接下载他们提供的jar包运行。
https://github.com/openzipkin/zipkin
下载并启动zipkin-server-2.12.9-exec.jar
启动命令:java -jar zipkin-server-2.12.9-exec.jar
导入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency>
编写配置文件
#指定服务的名称 spring: sleuth: sampler: probability: 1 # 采样率,百分之多少的sleuth信息需要输出到zipkin中,默认为0.1,值越大收集越及时,但性能影响也越大 zipkin: base-url: http://localhost:9411/ # 指定zipkin的地址
测试
测试 |
---|
九、完整SpringCloud架构图【重点
】
完整架构图 |
---|