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之中;能够便捷的把语言参数带到其他微服务之中。此方法值得推崇。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。