++(纯手打,代码可能有错!)++


服务间通讯

RestTemplate

  • 方式一

    直接使用restTemplate,url写死
  1. RestTemplate restTemplate = new RestTemplate();
  2. String responseStr = restTemplate.getForObject(“http://localhost:8080/msg”,String.class);
  • 方式二

    利用loadBalancerClient通过应用名获取URL,然后再使用restTemplate
  1. 先给controller注入loadBalancerClient
  1. @Autowired
  2. private LoadBalancerClient loadBalancerClient;
  1. 获取URL,再使用restTemplate
  1. RestTemplate restTemplate = new RestTemplate();
  2. ServiceInstance serviceInstance = loadBalancerClient.choose(“PRODUCT”);
  3. String url = String.format(“http://%s:%s”, serviceInstance.getHost, serviceInstance.getPort());
  4. String responseStr = restTemplate.getForObject(url, String.class);
  • 方式三

    利用@LoadBalanced注解,可在restTemplate里使用应用名字
  1. 利用@LoadBalanced注解
  1. @Bean
  2. @LoadBalanced
  3. public RestTemplate restTemplate(){
  4. return new RestTemplate();
  5. }
  1. String responseStr = restTemplate.getForObject(“http://PRODUCT/msg”, String.class);

Feign

  1. 添加依赖
  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-feign</artifactId>
  4. </dependency>
  1. 在主类中添加注解

    @EnableFeignClients
  2. 编写一个service链接其他服务

    在controller包同级目录下新建一个client包,在该包下新建ProductClient接口
  1. // product对应的是服务名
  2. @FeignClient(name="product")
  3. public interface ProductClient{
  4. @GetMapping("/msg")
  5. String productMsg();
  6. }

然后,在需要调用"/msg"接口的controller中这样使用:

  1. @Autowired
  2. private ProductClient productClient;

或添加其他服务的pom直接访问

统一配置中心

当要修改上线项目的一些配置、文案的时候,为了不用重复发行版本,只做一些小修改,就可以用统一配置中心。

  • 问题一:Config是怎么拿到码云上面的配置文件的?
  1. 创建一个springboot的配置中心服务端应用
  2. 引入依赖
  1. <!-- 引入cloud依赖 -->
  2. <dependencyManagement>
  3. <dependencies>
  4. <dependency>
  5. <groupId>org.springframework.cloud</groupId>
  6. <artifactId>spring-cloud-dependencies</artifactId>
  7. <version>Finchley.RELEASE</version>
  8. <type>pom</type>
  9. <scope>import</scope>
  10. </dependency>
  11. </dependencies>
  12. </dependencyManagement>
  1. <!-- 引入cloud配置中心服务端依赖 -->
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-config-server</artifactId>
  5. </dependency>
  6. <!-- 引入Eureka客户端依赖 -->
  7. <dependency>
  8. <groupId>org.springframework.cloud</groupId>
  9. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  10. </dependency>
  1. 修改配置文件,此处把配置放到了码云上面
  1. spring:
  2. application:
  3. name: config-server
  4. cloud:
  5. config:
  6. server:
  7. git:
  8. # 配置文件只搜索url目录下的searchPaths
  9. uri: git@gitee.com:szliugx/spring_cloud_config.git
  10. # 指定搜索路径,如果有多个路径则使用,分隔
  11. searchPaths: infomation/
  12. # 对于使用git,svn做为后端配置,从远程库获取配置文件,需要存储到本地文件
  13. basedir: /tmp/spring-cloud-repo
  14. # 配置中心通过git从远程git库,有时本地的拷贝被污染,这时配置中心无法从远程库更新本地配置,设置force-pull=true,则强制从远程库中更新本地库
  15. force-pull: true
  16. #username: username
  17. #password: password
  18. #服务注册中心端口号
  19. server:
  20. port: 6130
  21. #服务注册中心实例的主机名、端口
  22. #是否向服务注册中心注册自己
  23. #是否检索服务
  24. #服务注册中心的配置内容,指定服务注册中心的位置
  25. eureka:
  26. port:
  27. instance:
  28. hostname: localhost
  29. client:
  30. register-with-eureka: true
  31. fetch-registry: false
  32. serviceUrl:
  33. defaultZone: http://${eureka.instance.hostname}:${eureka.port}/eureka/
  1. 启动类上添加注释 @EnableConfigServer 和 @EnableEurekaClient
  1. @SpringBoorApplication
  2. @EnableConfigServer
  3. @EnableEurekaClient
  4. public class ConfigServerApplication {
  5. public static void main(String[] args){SpringApplication.run(ConfigServerApplication.class,args);}
  6. }

最后,为每个服务添加一个属于自己的配置文件在码云上

注意远程配置的命名,如 user-dev.properties,服务名 -环境.properties/服务名 -环境.yml

/{name}-{profiles}.yml

/{label}/{name}-{profiles}.yml

Name: 服务名

Profiles:环境

Label:分支(branch)

Spring Cloud Bus

配置.YML文件,开放所有的接口;

使用注解:@RefreshScope

异步和消息

例如,用户注册后,需要发短信和加积分。用户信息注册写入数据库后,通知异步消息,通知短信服务和积分服务做事情。

RabbitMQ

  1. 导入依赖Spring-boot-starter-amqp;
  2. 修改.YML,配置mq
  1. rabbbitmq:
  2. host: localhost
  3. port: 5671
  4. username: guest
  5. password: guest
  1. 编写代码

    接收端:
  1. /**
  2. * 水果商供应商服务 接收消息
  3. * @param message
  4. */
  5. @RabbitListener(bindings = @QueueBinding(
  6. exchange = @Exchange("myOrder"),
  7. key = "fruit",
  8. value = @Queue("fruitOrder")
  9. ))
  10. public void processFruit(String message){
  11. log.info("fruit MqReceiver: {}",message);
  12. }
  1. public class MqReceiver{
  2. // 1.@RabbitListener(queues = "myQueue")
  3. // 2.自动创建队列
  4. @RabbitListener(queuesToDeclare = @Queue("myQueue"))
  5. public void process(String message){
  6. log.info("MqReceiver: {}",message);
  7. }
  8. }

发送端:参数有(exchange、routingKey、message)

  1. @Component
  2. public class MqSenderTest extends OrderApplicationTests {
  3. @Autowired
  4. private AmqpTemplate amqpTemplate;
  5. @Test
  6. public void send(){
  7. amqpTemplate.converAndSend("myQueue", "now");
  8. }
  9. @Test
  10. public void sendOrder(){
  11. amqpTemplate.convertAndSend("myOrder", "computer");
  12. }
  13. }

Spring Cloud Stream

官方定义 Spring Cloud Stream 是一个构建消息驱动微服务的框架

应用程序通过 inputs 或者 outputs 来与 Spring Cloud Stream 中binder 交互,通过我们配置来 binding ,而 Spring Cloud Stream 的 binder 负责与消息中间件交互。所以,我们只需要搞清楚如何与 Spring Cloud Stream 交互就可以方便使用消息驱动的方式。

  1. 引入依赖:
  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
  4. </dependency>
  1. 配置文件中配置消息中间件RabbitMQ:
  1. rabbbitmq:
  2. host: localhost
  3. port: 5671
  4. username: guest
  5. password: guest
  1. 使用spring cloud stream发送和接收消息
  1. public interface StreamClient{
  2. String INPUT = "myMessage";
  3. @Input(StreamClient.INPUT)
  4. SubscribableChannel input();
  5. @Output(StreamClient.INPUT)
  6. MessageChannel output();
  7. }

消息接收端:

  1. @Component
  2. @EnableBinding(StreamClient.class)
  3. @Slf4j
  4. public class StreamReceiver{
  5. @StreamListener(StreamClient.INPUT)
  6. public void process(Object message){
  7. log.info("StreamReceiver: {}",message);
  8. }
  9. }

消息发送端:

  1. @RestController
  2. public class SendMessageController{
  3. @Autowired
  4. private StreamClient streamClient;
  5. @GetMapping("/sendMessage")
  6. public void process(){
  7. String message = "now" + new Date();
  8. streamClient.output().send(MessageBuilder.withPayload(message).build());
  9. }
  10. }

消息分组:

把一个服务放到一个组里面,不管这个服务有多少个实例,只会由一个实例来处理一个消息。

  1. spring:
  2. application:
  3. name: order
  4. cloud:
  5. config:
  6. discovery:
  7. enabled: true
  8. service-id: CONFIG
  9. profile: test
  10. stream:
  11. bindings:
  12. # 队列名称
  13. myMessage:
  14. # 组名称
  15. group: order

为了在MQ界面里面看到消息中java对象的具体内容,加一个配置:

  1. spring:
  2. application:
  3. name: order
  4. cloud:
  5. config:
  6. discovery:
  7. enabled: true
  8. service-id: CONFIG
  9. profile: test
  10. stream:
  11. bindings:
  12. # 队列名称
  13. myMessage:
  14. # 组名称
  15. group: order
  16. # 设置消息内容类型(可查看java对象属性)
  17. content-type: application/json

效果如下:

处理完消息之后,如果需要回应一下:

  1. 增加一个消息:
  1. public interface StreamClient {
  2. String INPUT = "myMessage";
  3. String INPUT2 = "myMessage2";
  4. @Input(StreamClient.INPUT)
  5. SubscribableChannel input();
  6. @Output(StreamClient.INPUT)
  7. MessageChannel output();
  8. @Input(StreamClient.INPUT2)
  9. SubscribableChannel input2();
  10. @Output(StreamClient.INPUT2)
  11. MessageChannel output2();
  12. }
  1. 用@SendTo注解返回信息:
  1. @StreamListener(value = StreamClient.INPUT)
  2. @SendTo(StreamClient.INPUT)
  3. public String process(OrderDTO message){
  4. log.info("StreamReceiver: {}",message);
  5. return "received";
  6. }
  7. @StreamListener(value = StreamClient.INPU2T)
  8. public String process2(OrderDTO message){
  9. log.info("StreamReceiver2: {}",message);
  10. return "received";
  11. }

异步扣库存分析

订单生成的时候(此时订单状态为等待中),向MQ发送信息,通知商品服务扣除库存,商品服务不论成功还是失败,都要返回结果给订单服务。订单服务订阅了商品服务,根据返回的信息,觉得这个订单是否生成。


服务网关

建议使用Nginx和zuul混搭的方式,使用nginx对外暴露一个URL,nginx把请求转发到多个zuul服务上,nginx继续做负载均衡,这样可以做到nginx和zuul的取长补短。

常见的网关方案:

Nginx+Lua

Spring Cloud Zuul

Spring Cloud Zuul

路由+过滤器 = Zuul

Zuul核心是一系列的过滤器

四种标准过滤器类型:

  • 前置Pre
  • 路由Route
  • 后置Post
  • 错误Error

要实现路由转发功能要加注解:@EnableZuulProxy

访问路径控制:(自定义、限制访问)

  1. zuul:
  2. routes:
  3. # /myProduct/product/list -> /product/product/list
  4. # product:
  5. # path: /myProduct/**
  6. # serviceId: product
  7. # 简洁写法
  8. product: /myProduct/**
  9. # 排除某些路由
  10. ignored-patterns:
  11. - /**/product/listForProduct

Cookie转发(默认是不能传cookie的):

默认限制了cookie、set-cookie、authorization的转发:

该字段设置为空即可放开:

  1. zuul:
  2. routes:
  3. # /myProduct/product/list -> /product/product/list
  4. product:
  5. path: /myProduct/**
  6. serviceId: product
  7. # 设置为空即可
  8. sensitiveHeaders:
  9. # 简洁写法
  10. product: /myProduct/**
  11. # 排除某些路由
  12. ignored-patterns:
  13. - /**/product/listForProduct

Zuul过滤器:

自定义preFilter:

  1. @Component
  2. public class TokenFilter extends ZuulFilter {
  3. @Override
  4. public String filterType() {
  5. return PRE_TYPE;
  6. }
  7. @Override
  8. public int filterOrder() {
  9. return PRE_DECORATION_FILTER_ORDER - 1;
  10. }
  11. @Override
  12. public boolean shouldFilter() {
  13. return true;
  14. }
  15. @Override
  16. public Object run() {
  17. RequestContext requestContext = RequestContext.getCurrentContext();
  18. HttpServletRequest request = requestContext.getRequest();
  19. //这里从url参数里获取, 也可以从cookie, header里获取
  20. String token = request.getParameter("token");
  21. if (StringUtils.isEmpty(token)) {
  22. requestContext.setSendZuulResponse(false);
  23. requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
  24. }
  25. return null;
  26. }
  27. }

自定义postFilter:

  1. @Component
  2. public class AddResponseHeaderFilter extends ZuulFilter{
  3. @Override
  4. public String filterType() {
  5. return POST_TYPE;
  6. }
  7. @Override
  8. public int filterOrder() {
  9. return SEND_RESPONSE_FILTER_ORDER - 1;
  10. }
  11. @Override
  12. public boolean shouldFilter() {
  13. return true;
  14. }
  15. @Override
  16. public Object run() {
  17. RequestContext requestContext = RequestContext.getCurrentContext();
  18. HttpServletResponse response = requestContext.getResponse();
  19. response.setHeader("X-Foo", UUID.randomUUID().toString());
  20. return null;
  21. }
  22. }

令牌桶限流(访问拦截):

  1. @Component
  2. public class RateLimitFilter extends ZuulFilter{
  3. //Google开源工具包Guava提供了限流工具类RateLimiter,该类基于令牌桶算法实现流量限制,使用十分方便,而且十分高效。
  4. private static final RateLimiter RATE_LIMITER = RateLimiter.create(100);
  5. @Override
  6. public String filterType() {
  7. return PRE_TYPE;
  8. }
  9. @Override
  10. public int filterOrder() {
  11. return SERVLET_DETECTION_FILTER_ORDER - 1;
  12. }
  13. @Override
  14. public boolean shouldFilter() {
  15. return true;
  16. }
  17. @Override
  18. public Object run() {
  19. //如果没有令牌,抛出异常
  20. if(!RATE_LIMITER.tryAcquire()){
  21. throw new RateLimitException();
  22. }
  23. return null;
  24. }
  25. }

微信商城中买家端和卖家端登录之后的区别:

买家端登录之后往cookie里写了一个openid=”openid”;

卖家端登录之后,在cookie中存了一个token="redisKey",这个redisKey一般是UUID,redis中存了一个redisKey="openid"

权限拦截:

例子:买家完成订单

  1. public class AuthSellerFilter extends ZuulFilter{
  2. @Autowired
  3. private StringRedisTemplate stringRedisTemplate;
  4. @Override
  5. public String filterType() {
  6. return PRE_TYPE;
  7. }
  8. @Override
  9. public int filterOrder() {
  10. return PRE_DETECTION_FILTER_ORDER - 1;
  11. }
  12. @Override
  13. public boolean shouldFilter() {
  14. RequestContext requestContext = RequestContext.getCurrentContext();
  15. HttpServletRequest request = requestContext.getRequest();
  16. if("/order/order/finish".equals(request.getRequest())){
  17. return true
  18. }
  19. return false;
  20. }
  21. @Override
  22. public Object run() {
  23. RequestContext requestContext = RequestContext.getCurrentContext();
  24. HttpServletRequest request = requestContext.getRequest();
  25. // /order/finish 只能卖家访问(cookie里面有token,并且对应redis中的值)
  26. Cookie cookie = CookieUtil.get(request,"token");
  27. if(cookie == null || StringUtils.isEmpty(cookie.getValue()) || StringUtils.isEmpty(stringRedisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_TEMPLATE, cookie.getValue())))){
  28. requestContext.setSendZuulResponse(false);
  29. requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
  30. }
  31. return null;
  32. }
  33. }

网关配置所有服务都可以传递cookie:

  1. zuul:
  2. # 全部服务忽略敏感头
  3. sensitive-headers:
  4. routes:

Zuul跨域:

  1. @Configuration
  2. public class CorsConfig {
  3. @Bean
  4. public CorsFilter corsFilter() {
  5. final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
  6. final CorsConfiguration config = new CorsConfiguration();
  7. config.setAllowCredentials(true);
  8. config.setAllowedOrgins(Arrays.asList("*"));
  9. config.setAllowedHeaders(Arrays.asList("*"));
  10. config.setAllowedMethods(Arrays.asList("*"));
  11. config.setMaxAge(300l);
  12. source.registerCorsConfiguration("/**",config);
  13. return new CorsFilter(source);
  14. }
  15. }

链路监控

Spring Cloud Sleuth

Spring Cloud Sleuth是Spring Cloud提供的分布式系统服务链追踪组件。

一个请求可能会经过多个服务才会得到结果,如果在这个过程中出现了异常,就很难去定位问题。所以,必须要实现一个分布式链路跟踪的功能,直观的展示出完整的调用过程。

  1. 引入依赖
  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-sleuth</artifactId>
  4. </dependency>
  1. 加入配置文件
  1. sleuth:
  2. sampler:
  3. # 1表示100%,所有日志都发送到外部程序展示,会消耗带宽等资源,只能在开发中使用
  4. percentage: 1

Zipkin

官网有安装方法:https://zipkin.io/pages/quickstart

重要概念:

TraceId: 全局跟踪ID,是跟踪的入口点。

SpanId: 下一层请求ID。一个traceId包含1个以上的spanId。

ParentId: 上一层请求跟踪ID,用来将前后的请求串联起来

  1. 引入依赖:
  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-sleuth-zipkin</artifactId>
  4. </dependency>

因为zipkin和sleuth需要一起使用,所以都需要导入依赖:

  1. <!--<dependency>-->
  2. <!--<groupId>org.springframework.cloud</groupId>-->
  3. <!--<artifactId>spring-cloud-starter-sleuth</artifactId>-->
  4. <!--</dependency>-->
  5. <!--<dependency>-->
  6. <!--<groupId>org.springframework.cloud</groupId>-->
  7. <!--<artifactId>spring-cloud-sleuth-zipkin</artifactId>-->
  8. <!--</dependency>-->
  9. <!-- 包含sleuth和zipkin -->
  10. <dependency>
  11. <groupId>org.springframework.cloud</groupId>
  12. <artifactId>spring-cloud-starter-zipkin</artifactId>
  13. </dependency>
  1. 修改配置文件:
  1. zipkin:
  2. base-url: http://localhost:9411/
  3. sleuth:
  4. sampler:
  5. percentage: 1

SpringCloud 随笔的更多相关文章

  1. springcloud问题随笔

    http://www.cnblogs.com/EasonJim/p/8085120.html 1.调用其它服务返回could not be queued for execution and no fa ...

  2. SpringCloud实战7-Config分布式配置管理

    分布式环境下的统一配置框架,已经有不少了,比如百度的disconf,阿里的diamand 官方文档对spring Cloud Config的描述如下:: Spring Cloud Config为分布式 ...

  3. SpringCloud实战8-Bus消息总线

    好了现在我们接着上一篇的随笔,继续来讲.上一篇我们讲到,我们如果要去更新所有微服务的配置,在不重启的情况下去更新配置,只能依靠spring cloud config了,但是,是我们要一个服务一个服务的 ...

  4. springcloud开篇

    微服务作为现在的常用架构,已经到了不学不行的地步.君不见spring官网https://spring.io/已经将springboot,springcloud,spring cloud data fl ...

  5. SpringCloud实战9-Stream消息驱动

    官方定义 Spring Cloud Stream 是一个构建消息驱动微服务的框架. 应用程序通过 inputs 或者 outputs 来与 Spring Cloud Stream 中binder 交互 ...

  6. springcloud(五)-Ribbon

    前言 先发句牢骚,最近太TM忙了,一直没时间静下心来继续写微服务架构!EMMMMMM..... 经过前文的讲解,我们已经实现了微服务的注册与发现.启动各个微服务时,Eureka Client会把自己的 ...

  7. SpringCloud(八):springcloud-bus消息总线(刷新配置服务)

    Bus消息总线: 好了现在我们接着上一篇的随笔,继续来讲.上一篇我们讲到,我们如果要去更新所有微服务的配置,在不重启的情况下去更新配置,只能依靠spring cloud config了,但是,是我们要 ...

  8. SpringCloud(七):springcloud-config统一管理配置中心

    前言: Spring Cloud Config组件是独立的,不需要注册到eureka.config工作原理是把读取目标到配置拉取到本地缓存一份然后供给其他客户端使用,所以一旦config启动成功,可以 ...

  9. SpringCloud Stream使用案例

    官方定义 Spring Cloud Stream 是一个构建消息驱动微服务的框架. 应用程序通过 inputs 或者 outputs 来与 Spring Cloud Stream 中binder 交互 ...

随机推荐

  1. Qt编写安防视频监控系统5-视频回放

    一.前言 一般视频回放都会采用GB28181国标来处理,这样可以保证兼容国内各大厂家的NVR,毕竟在同一的国家标准下,大家都会统一支持国标的,就不需要根据各个厂家的SDK来做兼容处理,烦得很,厂家越来 ...

  2. Python常用模块安装

    1. python操作MySQL数据库的依赖包MySQLdb ImportError: No module named MySQLdb 安装方式: yum install MySQL-python 2 ...

  3. CSS 自适应技巧

    DIV的内容垂直居中 不再MARGINT-TOP多少 来居中显示 display:table-cell; #block-1{ width:100%; height:80px; display:tabl ...

  4. supervieord的使用

    用途 守护进程,帮你管理其他进程,让其他进程成为后台进程 监控进程是否死掉,自动重启: 管理进程的启动,停止: 对进程输出的日志进行管理 每个进程使用不同的用户启动,这样可以使进程获得不同用户的权限 ...

  5. Flutter 状态管理 flutter_Provide

    项目的商品类别页面将大量的出现类和类中间的状态变化,这就需要状态管理.现在Flutter的状态管理方案很多,redux.bloc.state.Provide. Scoped Model : 最早的状态 ...

  6. 【转载】Bios报警声

    BB是报警的声音你可以根据报警声音长短,数目来判断问题出在什么地方 AWARD BIOS响铃声的一般含义是: 1短: 系统正常启动.这是我们每天都能听到的,也表明机器没有任何问题. 2短: 常规错误, ...

  7. 教程3 -如何与dotmemory内存优化交通

    在本教程中,我们将看到如何使用dotmemory优化你的应用程序的内存使用情况. 我们所说的什么“优化内存使用”?像在任何操作系统的过程中,垃圾收集(GC)消耗系统资源.逻辑很简单:更多的藏品GC已作 ...

  8. 32.网络编程TCP/UDP服务

    网络编程TCP: 服务器端口了解: port:0~65535 web服务:80 邮箱服务:556 0~1024:为服务默认的公认端口,一般我们不能用 套接字:socket socket作用 ip:po ...

  9. python基础关键词触发的魔法方法

    with: __enter__ __exit__ dic={} 获取容器中指定元素的行为 dic["name"] = "egon" #设置值 __setitem ...

  10. 文件input框选择图片实时显示小技巧

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...