SpringCloud网关(Zuul)如何给多个微服务之间传递共享参数

目录
  • 1、使用场景
    • 解决方案1
    • 解决方案2
  • 2、代码实现
  • 3、成果展现
  • 4、总结

1、使用场景

因为最近项目需要国际化,但是以前国际化的语言切换是放置在未进行微服务化之前的一个独立的SpringBoot服务之中。

目前由于业务的需要和不同模块能够复用的要求目前已经拆分为如下服务:zuul网关服务、**dx服务(包含主要服务)、**ai(一些推荐的AI服务)、message(消息咨询服务)、forum(论坛版块服务)等等。

如果还是按照以前的的方式只切换 **dx服务 之中的语言;其他微服务是不知道当前的Request请求是什么语言体系的。

就意味   **dx服务 是en语言,但是其他服务还是 zh(中文语言)。

因为此问题,我本人在项目开始进行国际化改版的时候,我就知道有此问题。 我就知道有此问题。 最开始就是有人不信;最后遇见问题了还是相信了!遇见问题了,总得使用相关的方案来解决此问题。

于是本人就是想到了有如下两种解决方案:

解决方案1

在网关服务(Zuul)之中,写一个统一的切换语言接口;在切换语言的使用同时调用其他服务的切换语言接口。这样实现起来最为简单。

具体如下图所示:

此方法比较原始直接,但是书写代码比较多;想当与每个微服务都得提供一个修改语言体系的接口供 网关(Zuul)

解决方案2

在网关(Zuul)服务层进行语言切换时候获得切换语言的参数;使用 LocaleContextHolder 暂存当前已经切换语言的信息;

然后在Zuul的过滤器之中通过获得当前请求(Request)的参数后,把以前语言切换暂存参数值从LocaleContextHolder获得切换语言的参数值。然后把这个参数传递到其他微服务之中;其他微服务通过Request 即可获得最新切换的语言参数;然后做到顺利获取切换后的最新的语言信息。

具体如下图所示:

针对方案一与方案二比较后;我选择使用的方案二。

2、代码实现

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String reqUrl=request.getRequestURL().toString();
        HttpSession session = request.getSession();
    	Map<String, Object> user=(Map<String, Object>) session.getAttribute("user");
        if(!(reqUrl.contains("permissionUser/userLogin") || (reqUrl.contains("noAuth"))
        		|| reqUrl.contains("appapi"))) {
        	String[] actProfile = env.getActiveProfiles();
    		String curProfile = actProfile[0];

        	if (StringUtils.startsWith(curProfile, "dev")) { //dev环境不做过滤
    			return null;
    		} 

    		//判断用户ID是否存在,不存在就跳转到登录界面
        	if (!checkUserIsLogin(ctx, user)) {
        	//	System.out.println("session丢失: " + request.getSession().getId());
        		return null;
        	}

            //放行ignoreUrls中配置的url
        	if(checkIsIgnoreUrl(request)) {
        		addUserToZuulRequestHeader(user);
        		return null;
        	}

        	//检查该url是否有权访问
        	if (!checkIsUrlHasRight(ctx)) {
        		return null;
        	}
        	//从会话之中获得当前登录用户的userId,判断用户是否被踢
        	if (checkUserIsKickout(ctx, user)) {
        		return null;
        	}
        }
        //可以往后走了,把session放入request的header中
        addUserToZuulRequestHeader(user);
        //语言包
        setLanguageLocal(request);
        return null;
    }

     /**
	 * 设置本地语言包
	 * */
	private void setLanguageLocal(HttpServletRequest request) {
		String lang = "zh";
		if (StringUtils.isNotEmpty(request.getParameter("lang"))) {
			lang = request.getParameter("lang");
		}else {
			lang=localeMessageSourceService.getCurrentLanguage();
		}
		switch(lang) {
			case "zh":
				LocaleContextHolder.setLocale(Locale.CHINESE);
				break;
			case "en":
				LocaleContextHolder.setLocale(Locale.ENGLISH);
				break;
			case "fr":
				LocaleContextHolder.setLocale(Locale.FRENCH);
				break;
		}
		//此处为获得请求的参数 然后在请求的参数里面加入暂存的语言携带参数 lang
		RequestContext ctx = RequestContext.getCurrentContext();
		request.getParameterMap();
		Map<String,List<String>> requestQueryParams=ctx.getRequestQueryParams();
		if(requestQueryParams==null) {
			requestQueryParams=new HashMap<>();
		}
		//String langCode=request.getParameter("lang").toString();
		//讲需要新增的参数添加进去,被调用的微服务可以直接获取,就像普通的一样;框架会直接注入进去
		    ArrayList<String> arrayList = new ArrayList<>();
            arrayList.add(lang);
            requestQueryParams.put("lang", arrayList);
            ctx.setRequestQueryParams(requestQueryParams);
		FrameWorkConstant.MSG_INFO_EMPTY = localeMessageSourceService.getMessage("msg.info.empty");
		FrameWorkConstant.MSG_INFO_SUCCESS = localeMessageSourceService.getMessage("msg.success.operate");
		FrameWorkConstant.MSG_INFO_FAILED= localeMessageSourceService.getMessage("msg.error.unknown");
	}

ChangeLanauageConfigurer

import java.util.Locale;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;

/***
 *@purpose:国际化启动配置类
 *@since:2018年4月9日
 ***/
@Configuration
public class ChangeLanauageConfigurer extends WebMvcConfigurerAdapter {

	/**
	 * 描述 : 国际化配置类(会话区域解析器)
	 *
	 */
	@Bean
    public SessionLocaleResolver localeResolver() {
        SessionLocaleResolver slr = new SessionLocaleResolver();
//        System.out.println("aaaaaaaaaaaaaaaaaaaaaa");
//        // 设置默认语言
        slr.setDefaultLocale(Locale.CHINESE);
        return slr;
    }

    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
        // 设置参数名
        lci.setParamName("lang");
        return lci;
    }

    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }
}

LocaleMessageSourceService

/***
 *@purpose:本地化语言信息工具类
 *@since:2018年3月30日
 ***/
@Service
public class LocaleMessageSourceService {

	@Autowired
	private MessageSource messageSource;

    /**
    *
    *@param code:对应messages配置的key.
    *@return
    */

   public String getMessage(String code){

      //这里使用比较方便的方法,不依赖request.
      Locale locale = LocaleContextHolder.getLocale();
      String lang = locale.getLanguage();
      if(LanguageConstant.LANGUAGE_FR.equals(lang)) {
    	  locale = new Locale(lang,"FR");
      }
      if(LanguageConstant.LANGUAGE_ZH.equals(lang)) {
    	  locale = new Locale(lang,"CN");
      }
      if(LanguageConstant.LANGUAGE_EN.equals(lang)) {
    	  locale = new Locale(lang,"US");
      }

      String message = messageSource.getMessage(code,null,locale);
      return message;
   }

   /**
             * 获得当前语言
    * @param code
    * @return
    */
   public String getCurrentLanguage(){

       Locale locale = LocaleContextHolder.getLocale();
       String lang = locale.getLanguage();
       if(StringUtils.isEmpty(lang)) {
       	lang = LanguageConstant.LANGUAGE_EN;
       }
       return lang;
    }
}

3、成果展现

在网关服务之中切换语言(en)后获得信息

ai服务的语言体系

**dx服务的语言体系

网关切换为中文(zh)后其他服务语言返回

ai服务语言切换结果

**dx服务语言切换结果

4、总结

如果使用简单粗暴的方法,直接通过网关调用其他微服务;虽然能够实现功能;但是此方法比较笨重;同时书写的代码比较多。为后期维护带来很大的不便利。

如果使用方案二在网关层进行统一处理,借助语言切换的能够在网关(Zuul)服务进行暂存和传递;同时动态添加切换语言后的参数到Request之中;能够便捷的把语言参数带到其他微服务之中。此方法值得推崇。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • springcloud 中 zuul 修改请求参数信息的方法

    Zuul是Netflix出品的一个基于JVM路由和服务端的负载均衡器. Zuul功能: 认证 压力测试 金丝雀测试 动态路由 负载削减 安全 静态响应处理 主动/主动交换管理 Zuul的规则引擎允许通过任何JVM语言来编写规则和过滤器, 支持基于Java和Groovy的构建. 配置属性 zuul.max.host.connections 已经被两个新的配置属性替代, zuul.host.maxTotalConnections (总连接数)和 zuul.host.maxPerRouteConnec

  • 详解Spring Cloud Zuul中路由配置细节

    上篇文章我们介绍了API网关的基本构建方式以及请求过滤,小伙伴们对Zuul的作用应该已经有了一个基本的认识,但是对于路由的配置我们只是做了一个简单的介绍,本文我们就来看看路由配置的其他一些细节. 首先我们来回忆一下上篇文章我们配置路由规则的那两行代码: zuul.routes.api-a.path=/api-a/** zuul.routes.api-a.serviceId=feign-consumer 我们说当我的访问地址符合/api-a/**规则的时候,会被自动定位到feign-consume

  • spring cloud 使用Zuul 实现API网关服务问题

    通过前面几次的分享,我们了解了微服务架构的几个核心设施,通过这些组件我们可以搭建简单的微服务架构系统.比如通过Spring Cloud Eureka搭建高可用的服务注册中心并实现服务的注册和发现: 通过Spring Cloud Ribbon或Feign进行负载均衡:通过Spring Cloud Hystrix进行服务容错保护以避免故障蔓延.微服务搭建好了之后我们肯定会提供给外部系统一些统一的RESTFul API服务接口进行调用, 但是当外部系统调用我们的RESTful API的时候,怎么确定它

  • 详解Spring Cloud Zuul 服务网关

    有了Eureka服务注册发现.Hystrix断路器.Ribbon服务调用负载均衡,以及spring cloud config 集群配置中心,似乎一个微服务框架已五脏俱全,last but not least,一个服务网关却不可或缺. Spring Cloud Zuul路由是微服务架构的不可或缺的一部分,提供动态路由,监控,弹性,安全等的边缘服务.Zuul是Netflix出品的一个基于JVM路由和服务端的负载均衡器. Zuul介绍 在整个Spring Cloud微服务框架里,Zuul扮演着"智能网

  • SpringCloud网关(Zuul)如何给多个微服务之间传递共享参数

    目录 1.使用场景 解决方案1 解决方案2 2.代码实现 3.成果展现 4.总结 1.使用场景 因为最近项目需要国际化,但是以前国际化的语言切换是放置在未进行微服务化之前的一个独立的SpringBoot服务之中. 目前由于业务的需要和不同模块能够复用的要求目前已经拆分为如下服务:zuul网关服务.**dx服务(包含主要服务).**ai(一些推荐的AI服务).message(消息咨询服务).forum(论坛版块服务)等等. 如果还是按照以前的的方式只切换 **dx服务 之中的语言:其他微服务是不知

  • SpringCloud实现Redis在各个微服务的Session共享问题

    在微服务中,需要我们在各个微服务中共享Session,使用Redis来共享Session是一个很好的解决方法,Redis是运行在内存中,查取速度很快. 1.pom文件中添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> </dependency> <depe

  • SpringCloud Feign实现微服务之间相互请求问题

    目录 Feign简介 Spring Cloud 组件依赖版本 Feign实现服务之间访问 ☘创建nacos-consumer-feign微服务 创建feign client ☘nacos-provider微服务 Feign微服务之间访问测试 ☘Feign容错机制 上篇文章说了通过RestTemplate实现微服务之间访问:https://www.jb51.net/article/252981.htm,这篇文章将通过Feign实现微服务之间访问.代码基于RestTemplate实现微服务之间访问基

  • springcloud 如何解决微服务之间token传递问题

    目录 微服务之间token传递问题 服务A中FeginInterceptor 服务A添加配置文件 微服务服务间调用传递token RequestInterceptor是feign提供的接口 微服务之间token传递问题 假设现在有A服务,B服务,外部使用RESTApi请求调用A服务,在请求头上有token字段,A服务使用完后,B服务也要使用,如何才能把token也转发到B服务呢? 这里可以使用Feign的RequestInterceptor,但是直接使用一般情况下HttpServletReque

  • Spring Cloud Alibaba Nacos服务治理平台,服务注册、RestTemplate实现微服务之间访问负载均衡访问的问题

    目录 Nacos简介 ☘Spring Cloud 组件依赖版本 ☘Nacos部署 ☘访问Nacos平台 Nacos服务注册.微服务访问.负载均衡实现 nacos-consumer微服务创建 ☘nacos-provider微服务创建 测试 Nacos简介 Github:https://github.com/alibaba/nacos官网文档:https://nacos.io/zh-cn/docs/what-is-nacos.htmlNacos 提供了发现.配置和管理微服务能力,能快速实现动态服务发

  • spring cloud eureka微服务之间的调用详解

    微服务之间的调用如何实现 首先 你需要两个或以上的微服务模块 至于怎么创建可以参考我上一篇博客 spring cloud eureka注册中心 如果想在页面显示 那么需要先加上 compile 'org.springframework.boot:spring-boot-starter-thymeleaf' 这个thymeleaf依赖 springboot推荐使用thymeleaf模板 它的最大好处就是原型即是模板 后缀是html html文件 需要放在resources/templates文件夹

  • Spring Cloud多个微服务之间调用代码实例

    这篇文章主要介绍了Spring Cloud多个微服务之间调用代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 现在又一个学生微服务 user 和 学校微服务 school,如果user需要访问school,我们应该怎么做? 1.使用RestTemplate方式 添加config import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.spr

  • 教你Spring Cloud保证各个微服务之间调用安全性

    导读:在微服务的架构下,系统会根据业务拆分为多个服务,各自负责单一的职责,在这样的架构下,我们需要确保各api的安全性,也就是说服务不是开放的,而是需要授权才可访问的,避免接口被不合法的请求所访问. 但是在在微服务集群中服务之间暴力的接口,或者对于第三方开放的接口如果不做及安全和认证,后果可想而知. 阅读下文之前思考几个问题: 如何在restTemplate远程调用请求增加添加统一认证? 服务认证如何规范加密和解密? 远程调用统一什么协议比较合适? 如下图,三个服务注册到同一个注册中心集群,服务

  • 微服务之间如何通过feign调用接口上传文件

    具体需求: 我们的项目是基于springboot框架的springcloud微服务搭建的,后端服务技术层面整体上分为business服务和core服务,business服务用于作为应用层,直接连接客户端,通常用于聚合数据,core服务用来客户端具体操作不同需求来控制数据库,文件上传是通过客户端上传接口,通过business服务,由服务端调用feign接口,也是第一次做这种文件中转,遇到各种问题,下面是我自己的解决方案,不喜勿喷,代码小白一枚; 一.core服务层接口@requestmapping

  • spring cloud中微服务之间的调用以及eureka的自我保护机制详解

    上篇讲了spring cloud注册中心及客户端的注册,所以这篇主要讲一下服务和服务之间是怎样调用的 不会搭建的小伙伴请参考我上一篇博客:idea快速搭建spring cloud-注册中心与注册 基于上一篇的搭建我又自己搭建了一个客户端微服务: 所以现在有两个微服务,我们所实现的就是微服务1和微服务2之间的调用 注册中心就不用多说了,具体看一下两个微服务 application.yml配置也不用说了,不知道怎么配置的请参考我上篇博客 在project-solr中的constroller中: @R

随机推荐