SpringCloud網(wǎng)關(guān)(Zuul)如何給多個(gè)微服務(wù)之間傳遞共享參數(shù)
1、使用場(chǎng)景
因?yàn)樽罱?xiàng)目需要國(guó)際化,但是以前國(guó)際化的語(yǔ)言切換是放置在未進(jìn)行微服務(wù)化之前的一個(gè)獨(dú)立的SpringBoot服務(wù)之中。
目前由于業(yè)務(wù)的需要和不同模塊能夠復(fù)用的要求目前已經(jīng)拆分為如下服務(wù):zuul網(wǎng)關(guān)服務(wù)、**dx服務(wù)(包含主要服務(wù))、**ai(一些推薦的AI服務(wù))、message(消息咨詢服務(wù))、forum(論壇版塊服務(wù))等等。
如果還是按照以前的的方式只切換 **dx服務(wù) 之中的語(yǔ)言;其他微服務(wù)是不知道當(dāng)前的Request請(qǐng)求是什么語(yǔ)言體系的。
就意味 **dx服務(wù) 是en語(yǔ)言,但是其他服務(wù)還是 zh(中文語(yǔ)言)。
因?yàn)榇藛栴},我本人在項(xiàng)目開始進(jìn)行國(guó)際化改版的時(shí)候,我就知道有此問題。 我就知道有此問題。 最開始就是有人不信;最后遇見問題了還是相信了!遇見問題了,總得使用相關(guān)的方案來解決此問題。
于是本人就是想到了有如下兩種解決方案:
解決方案1
在網(wǎng)關(guān)服務(wù)(Zuul)之中,寫一個(gè)統(tǒng)一的切換語(yǔ)言接口;在切換語(yǔ)言的使用同時(shí)調(diào)用其他服務(wù)的切換語(yǔ)言接口。這樣實(shí)現(xiàn)起來最為簡(jiǎn)單。
具體如下圖所示:

此方法比較原始直接,但是書寫代碼比較多;想當(dāng)與每個(gè)微服務(wù)都得提供一個(gè)修改語(yǔ)言體系的接口供 網(wǎng)關(guān)(Zuul)
解決方案2
在網(wǎng)關(guān)(Zuul)服務(wù)層進(jìn)行語(yǔ)言切換時(shí)候獲得切換語(yǔ)言的參數(shù);使用 LocaleContextHolder 暫存當(dāng)前已經(jīng)切換語(yǔ)言的信息;
然后在Zuul的過濾器之中通過獲得當(dāng)前請(qǐng)求(Request)的參數(shù)后,把以前語(yǔ)言切換暫存參數(shù)值從LocaleContextHolder獲得切換語(yǔ)言的參數(shù)值。然后把這個(gè)參數(shù)傳遞到其他微服務(wù)之中;其他微服務(wù)通過Request 即可獲得最新切換的語(yǔ)言參數(shù);然后做到順利獲取切換后的最新的語(yǔ)言信息。
具體如下圖所示:

針對(duì)方案一與方案二比較后;我選擇使用的方案二。
2、代碼實(shí)現(xiàn)
@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環(huán)境不做過濾
return null;
}
//判斷用戶ID是否存在,不存在就跳轉(zhuǎn)到登錄界面
if (!checkUserIsLogin(ctx, user)) {
// System.out.println("session丟失: " + request.getSession().getId());
return null;
}
//放行ignoreUrls中配置的url
if(checkIsIgnoreUrl(request)) {
addUserToZuulRequestHeader(user);
return null;
}
//檢查該url是否有權(quán)訪問
if (!checkIsUrlHasRight(ctx)) {
return null;
}
//從會(huì)話之中獲得當(dāng)前登錄用戶的userId,判斷用戶是否被踢
if (checkUserIsKickout(ctx, user)) {
return null;
}
}
//可以往后走了,把session放入request的header中
addUserToZuulRequestHeader(user);
//語(yǔ)言包
setLanguageLocal(request);
return null;
}
/**
* 設(shè)置本地語(yǔ)言包
* */
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;
}
//此處為獲得請(qǐng)求的參數(shù) 然后在請(qǐng)求的參數(shù)里面加入暫存的語(yǔ)言攜帶參數(shù) 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();
//講需要新增的參數(shù)添加進(jìn)去,被調(diào)用的微服務(wù)可以直接獲取,就像普通的一樣;框架會(huì)直接注入進(jìn)去
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:國(guó)際化啟動(dòng)配置類
*@since:2018年4月9日
***/
@Configuration
public class ChangeLanauageConfigurer extends WebMvcConfigurerAdapter {
/**
* 描述 : 國(guó)際化配置類(會(huì)話區(qū)域解析器)
*
*/
@Bean
public SessionLocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
// System.out.println("aaaaaaaaaaaaaaaaaaaaaa");
// // 設(shè)置默認(rèn)語(yǔ)言
slr.setDefaultLocale(Locale.CHINESE);
return slr;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
// 設(shè)置參數(shù)名
lci.setParamName("lang");
return lci;
}
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
}LocaleMessageSourceService
/***
*@purpose:本地化語(yǔ)言信息工具類
*@since:2018年3月30日
***/
@Service
public class LocaleMessageSourceService {
@Autowired
private MessageSource messageSource;
/**
*
*@param code:對(duì)應(yīng)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;
}
/**
* 獲得當(dāng)前語(yǔ)言
* @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、成果展現(xiàn)

在網(wǎng)關(guān)服務(wù)之中切換語(yǔ)言(en)后獲得信息

ai服務(wù)的語(yǔ)言體系

**dx服務(wù)的語(yǔ)言體系

網(wǎng)關(guān)切換為中文(zh)后其他服務(wù)語(yǔ)言返回

ai服務(wù)語(yǔ)言切換結(jié)果

**dx服務(wù)語(yǔ)言切換結(jié)果

4、總結(jié)
如果使用簡(jiǎn)單粗暴的方法,直接通過網(wǎng)關(guān)調(diào)用其他微服務(wù);雖然能夠?qū)崿F(xiàn)功能;但是此方法比較笨重;同時(shí)書寫的代碼比較多。為后期維護(hù)帶來很大的不便利。
如果使用方案二在網(wǎng)關(guān)層進(jìn)行統(tǒng)一處理,借助語(yǔ)言切換的能夠在網(wǎng)關(guān)(Zuul)服務(wù)進(jìn)行暫存和傳遞;同時(shí)動(dòng)態(tài)添加切換語(yǔ)言后的參數(shù)到Request之中;能夠便捷的把語(yǔ)言參數(shù)帶到其他微服務(wù)之中。此方法值得推崇。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java獲取當(dāng)前時(shí)間并轉(zhuǎn)化為yyyy-MM-dd?HH:mm:ss格式的多種方式
這篇文章主要介紹了Java獲取當(dāng)前時(shí)間并轉(zhuǎn)化為yyyy-MM-dd?HH:mm:ss格式的多種方式,每種方式結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-03-03
mybatisplus?selectOne查詢,有數(shù)據(jù),但返回為null問題
這篇文章主要介紹了mybatisplus?selectOne查詢,有數(shù)據(jù),但返回為null問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11
詳解MyBatis Mapper 代理實(shí)現(xiàn)數(shù)據(jù)庫(kù)調(diào)用原理
這篇文章主要介紹了詳解MyBatis Mapper 代理實(shí)現(xiàn)數(shù)據(jù)庫(kù)調(diào)用原理,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
SpringBoot對(duì)Jar包進(jìn)行加密的示例代碼
在當(dāng)今互聯(lián)網(wǎng)高速發(fā)展的時(shí)代,軟件安全問題變得尤為突出,隨著開源技術(shù)的普及,Spring Boot等框架的應(yīng)用越來越廣泛,但也伴隨著代碼被反編譯的風(fēng)險(xiǎn),本文將探討在Spring Boot項(xiàng)目中對(duì)Jar包進(jìn)行加密,以防止反編譯的安全實(shí)踐,需要的朋友可以參考下2023-12-12
Java ==,equals()與hashcode()的使用
本文主要介紹了Java ==,equals()與hashcode()的使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05

