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

 更新时间:2022年03月22日 10:06:08   作者:蜀山雪松  
这篇文章主要介绍了SpringCloud网关(Zuul)如何给多个微服务之间传递共享参数,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

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之中;能够便捷的把语言参数带到其他微服务之中。此方法值得推崇。

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

相关文章

  • Spring Boot Security配置教程

    Spring Boot Security配置教程

    在本文里我们给大家分享了关于Spring Boot Security配置的相关步骤以及注意要点,需要的朋友们跟着操作下。
    2019-05-05
  • MyBatis中批量插入和批量更新的实现方法详解

    MyBatis中批量插入和批量更新的实现方法详解

    这篇文章主要介绍了MyBatis中批量插入和批量更新的实现方法,在日常开发中有时候需要从A数据库提取大量数据同步到B系统,这种情况自然是需要批量操作才行,感兴趣想要详细了解可以参考下文
    2023-05-05
  • Java LinkedHashSet集合的底层原理和TreeSet集合

    Java LinkedHashSet集合的底层原理和TreeSet集合

    LinkedHashSet保证元素有序且唯一,底层通过双链表实现,TreeSet元素不重复且可排序,底层使用红黑树实现排序,自定义类型排序可通过实现Comparable接口或提供Comparator来定义排序规则,适用于需要大量元素快速检索的场景
    2024-10-10
  • 使用Java实现价格加密与优化功能

    使用Java实现价格加密与优化功能

    在现代软件开发中,数据加密是一个非常重要的环节,尤其是在处理敏感信息(如价格、用户数据等)时,本文将详细介绍如何使用 Java 实现价格加密,并对代码进行优化,需要的朋友可以参考下
    2025-01-01
  • 实例讲解Java批量插入、更新数据

    实例讲解Java批量插入、更新数据

    这片文章介绍了一个Java批量添加数据,多个字段同时添加多条数据具体实例,面向的是Oracle数据库,需要的朋友可以参考下
    2015-07-07
  • Java异常分类处理抛出捕获操作详解

    Java异常分类处理抛出捕获操作详解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等
    2022-09-09
  • SpringDataRedis入门和序列化方式解决内存占用问题小结

    SpringDataRedis入门和序列化方式解决内存占用问题小结

    spring-data-redis是spring-data模块的一部分,专门用来支持在spring管理项目对redis的操作,这篇文章主要介绍了SpringDataRedis入门和序列化方式解决内存占用问题,需要的朋友可以参考下
    2022-12-12
  • resubmit渐进式防重复提交框架示例

    resubmit渐进式防重复提交框架示例

    这篇文章主要为大家介绍了resubmit渐进式防重复提交框架示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • 使用@PathVariable时候无法将参数映射到变量中的解决

    使用@PathVariable时候无法将参数映射到变量中的解决

    这篇文章主要介绍了使用@PathVariable时候无法将参数映射到变量中的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • SpringBoot整合RabbitMQ及生产全场景高级特性实战

    SpringBoot整合RabbitMQ及生产全场景高级特性实战

    本文主要介绍了SpringBoot整合RabbitMQ及生产全场景高级特性实战,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-10-10

最新评论