Spring Cloud 搭建基礎綜合框架【實操】
>> '<搭建說明>:'
> '使用的元件包括: Eureka、Ribbon、Config、Zuul、Hystrix 完成一個使用者資訊管理小服務。'
> '後端框架包括:註冊中心Eureka、配置中心 Config API閘道器Zuul、客戶端負載均衡Ribbon、斷路器Hystrix;'
> '同時後端包含兩個業務服務,一個是使用者服務sc-user-server,一個是資料服務sc-data-server'
>> 技術方案實現流程圖間 圖-1
> 1)'使用者從瀏覽器發起請求',經過瀏覽器,請求達到Nginx,
> 2)開啟前端介面,由前端發起請求後臺資料,
> 3)當請求達到Nginx後,'Nginx對閘道器層進行負載',因為閘道器也需要做'HA'(HA是什麼?底部有註腳定義),
> 4)此時閘道器收到請求後會根據請求路徑進行'動態路由',
> 5)根據'服務名發現'是 UserService中的服務,則從Ribbon中選擇一臺UsrService的例項進行呼叫
> 6)由UserServer返回資料,如果'此時UserService需要使用第三方DataService返回資料',
> 7)'則跟Zuul一樣,選擇一臺DataService的例項進行呼叫',返回資料到前臺即可渲染頁面
> 8)流程結束?
-HA(Highly Available)(雙機叢集(HA)系統簡稱),高可用性叢集,是保證業務連續性的有效解決方案,
-一般有兩個或兩個以上的節點,且分為活動節點及備用節點。
工程名 | 埠 | 描述 |
---|---|---|
server | N/A | 父工程 |
config-server | 9090 | 配置中心 |
eureka-server | 8761 | 註冊中心 |
zuul-server | 7777 | API GateWay 閘道器 |
hystrix-dashboard | 9099 | 儀表盤& Turbine(風機)聚合Hystrix 斷路器 -整合整個叢集下的監控狀態 |
common | N/A | 公共基礎包,方便後臺服務引用 |
user-server | 9091 | 使用者服務,對使用者資料的操作 |
data-server | 8099 | API 資料服務,提供基礎的資料 |
<!-父工程-server->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
> config-server |9090| 配置中心:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
server:
port: 9090
spring:
cloud:
config:
server:
git:
uri: #配置檔案遠端地址URL
#username:
#password:
search-paths: SC-CONFIG
application:
name: sc-configserver
/*** @description : 配置中心 */
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);}}
> eureka-server | 8761| 註冊中心:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
server:
port: 8761
spring:
application:
name: sc-eurekaserver
eureka:
client:
registerWithEureka: false
fetchRegistry: false
/*** eureka server*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);}}
> zuul-server | 7777| API GateWay 閘道器:
>> <Zuul中的Fallback機制:>
> 在微服務應用本身發生問題後,Zuul提供類一個Fallback機制,可以在出現問題時候進行統一處理,
> 需要實現FallbackProvider介面,然後定義自己需要的錯誤碼和錯誤資訊即可。
> -: 實現FallbackProvider介面,加上相應的處理邏輯,在Zuul-Fallback.java 該類中。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>cn.springcloud.book</groupId>
<artifactId>common</artifactId>
<version>${parent.version}</version>
</dependency>
eureka:
client:
serviceUrl:
defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8761}/eureka/
instance:
prefer-ip-address: true
management:
security:
enabled: false
endpoints:
web:
exposure:
include: hystrix.stream
feign:
hystrix:
enabled: true
ribbon:
ConnectTimeout: 6000
ReadTimeout: 6000
MaxAutoRetries: 0 #對第一次請求的服務的重試次數
MaxAutoRetriesNextServer: 0 #要重試的下一個服務的最大數量(不包括第一個服務)
OkToRetryOnAllOperations: false
zuul:
ribbonIsolationStrategy: THREAD
threadPool:
useSeparateThreadPools: true
threadPoolKeyPrefix: zuulgateway
max:
host:
max-per-route-connections: 200
max-total-connections: 500
host:
socket-timeout-millis: 5000
connect-timeout-millis: 10000
hystrix:
threadpool:
default:
coreSize: 20
maximumSize: 50
maxQueueSize: -1
allowMaximumSizeToDivergeFromCoreSize: true
command:
default:
execution:
timeout:
enabled: false
isolation:
thread:
interruptOnTimeout: false
timeoutInMilliseconds: 15000
spring:
application:
name: sc-zuul-server
server:
port: 7777
> 從閘道器正確的訪問http://localhost:7777/sc-user/service/getContextUserId 這個地址,此時發現報錯:
> 這是自定義類一個異常,沒有傳使用者資訊,因為這裡在閘道器做了攔截,
> 如果請求頭裡沒有x-customs-user 則鑑權不通過,
> 程式碼如下:AuthFilter
/*** 鑑權filter*/
public class AuthFilter extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(AuthFilter.class);
@Override
public boolean shouldFilter() {// 判斷是否需要進行處理return true;}
@Override
public Object run() {
RequestContext rc = RequestContext.getCurrentContext();
authUser(rc);return null;}
@Override
public String filterType() {return "pre";}
@Override
public int filterOrder() {return 0;}
private static Map<String, String> httpRequestToMap(HttpServletRequest request) {
Enumeration<String> headerNames = request.getHeaderNames();
Map<String, String> headers = new HashMap<>();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
headers.put(headerName, request.getHeader(headerName));
}return headers;}
public static void authUser(RequestContext ctx) {
HttpServletRequest request = ctx.getRequest();
Map<String, String> header = httpRequestToMap(request);
String userId = header.get(User.CONTEXT_KEY_USERID);
if(StringUtils.isEmpty(userId)) {
try {
BaseException BaseException = new
BaseException(CommonError.AUTH_EMPTY_ERROR.getCode(),CommonError.
AUTH_EMPTY_ERROR.getCodeEn(),CommonError.
AUTH_EMPTY_ERROR.getMessage(),1L);
BaseExceptionBody errorBody = new BaseExceptionBody(BaseException);
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
ctx.setResponseBody(JSONObject.toJSON(errorBody).toString());
} catch (Exception e) {logger.error("println message error",e);}
}else {
for (Map.Entry<String, String> entry : header.entrySet()) {
ctx.addZuulRequestHeader(entry.getKey(), entry.getValue());}}}
@Component
public class ZuulFallback implements FallbackProvider{
@Override
public String getRoute() {
return "*"; //可以配置指定的路由,值為serviceId,如sc-user-service}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.INTERNAL_SERVER_ERROR;}
@Override
public String getStatusText() throws IOException {
return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();}
@Override
public void close() {}
@Override
public InputStream getBody() throws IOException {
//定義自己的錯誤資訊
return new ByteArrayInputStream(("microservice error").getBytes()); }
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers; }
@Override
public int getRawStatusCode() throws IOException {
// TODO Auto-generated method stub
return HttpStatus.INTERNAL_SERVER_ERROR.value();}};}}
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
@EnableCircuitBreaker
public class ZuulServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulServerApplication.class, args);}
@Bean
public AuthFilter preRequestFilter() {return new AuthFilter();}}
>hystrix-dashboard | 9099| 儀表盤& Turbine(風機)聚合Hystrix 斷路器 -整合整個叢集下的監控狀態:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
eureka:
client:
serviceUrl:
defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8761}/eureka/
instance:
prefer-ip-address: true
management:
security:
enabled: false
endpoints:
web:
exposure:
include: hystrix.stream
turbine:
appConfig: sc-user-service,sc-zuul-service,sc-data-service
clusterNameExpression: "'default'"
server:
port: 9099
spring:
cloud:
config:
label: master
uri: http://localhost:9090
name: config-info
profile: dev
application:
name: sc-hystrix-dashboard
@SpringBootApplication
@EnableDiscoveryClient
@EnableTurbine
@EnableHystrixDashboard
public class HystrixDashboardTurbineApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardTurbineApplication.class, args);}}
> common | N/A| 公共基礎包,方便後臺服務引用:
>> '公共包(物件,攔截器,工具類等)'
> 框架一般會有一些值物件,攔截器,分頁物件,許可權等這些基礎資料,
> 並且其它服務都是需要共有的能力的,所以會抽出這部分物件放入公共包裡,
> 供其它服務引用。建立common的工程.
> 如 user-service 和 data-service 都會用到它裡面一些元件和類。
> 該包裡存有/存放的類或東西可以有如下幾部分:
> 1)'使用者上下文物件傳遞',
> 1-1: 如使用者物件,從Zuul閘道器一直到後面的微服務都需要使用者物件
> 1-2: 後面的微服務需要獲取到使用者ID後開展非一些業務操作,比如:
> 1-2-1:在 Zuul獲取到使用者資訊,存入Header頭,後臺服務進入方法前,'獲取到Header進行組裝User使用者物件',
> 1-2-1:'後臺服務通過UserContextHolder獲取'。
> 1-2-2:在後臺服務之間相互呼叫時,增加攔截器,獲取當前使用者然後轉換成Header放入請求頭,
> 1-2-3:被'呼叫服務攔截器後解析到Header放入上下文',服務通過UserContextHolder獲取。
> 1-3:具體實現步驟:
> 1-3-1:一呢,在公共SDK裡定義使用者物件User。
> 1-3-2:二呢,增加三個攔截器。
> 1-3-3:FeignUserContextInterceptor類中,在使用Feign進行服務間呼叫時會攔截到請求,
> 並將使用者屬性放到Header裡,程式碼在類內。
> RestTemplateUserContextInterceptor類,在使用使用RestTemplate進行服務之間呼叫時,會攔截到請求
> 並將使用者屬性放到Header裡,該類中有具體實現步驟。
> UserContexttInterceptor類,進入controller控制器時,會攔截到請求,
> 從Header頭解析出使用者物件存入上下文中,方便服務裡使用,具體實現程式碼檢視該類。
> 1-3-4:三呢,增加UserContextHolder類。
> 1-3-5: Hystrix併發策略,檢視SpringCloudHystrixConcurrencyStrategy類具體實現。
> 此類使用了 'ThreadLocal 物件'儲存使用者資訊,由於線上執行緒池隔離的模式下,會導致前後執行緒傳遞物件丟失,
> 該類中,使用自定義併發策略 HystrixConcurrencyStrategy解決此問題,具體方法看該類的實現。
> 1-3-6:在配置類中註冊三個攔截器和併發策略,程式碼看CommonConfiguration.java 類。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.31</version>
</dependency>
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.springcloud.book.common.config.CommonConfiguration
/*** 使用者物件 **/
public class User implements Serializable {
private static final long serialVersionUID = -4083327605430665846L;
public final static String CONTEXT_KEY_USERID = "x-customs-user";
/** * 使用者ID*/
private String userId;
private String userName;
public String getUserName() {return userName;}
public void setUserName(String userName) {this.userName = userName;}
public String getUserId() {return userId;}
public void setUserId(String userId) {this.userId = userId;}
public User() {}
public User(Map<String, String> headers) {
userId = headers.get(CONTEXT_KEY_USERID);}
/*** 將user物件轉換成為http物件頭* @return http頭鍵值對*/
public Map<String, String> toHttpHeaders() {
Map<String, String> headers = new HashMap<>();
headers.put(CONTEXT_KEY_USERID,userId);
return headers;}}
public class HttpConvertUtil {
/*** convert the httpServletRequest headers to headers map
* @param request* @return*/
public static Map<String, String> httpRequestToMap(HttpServletRequest request) {
Enumeration<String> headerNames = request.getHeaderNames();
Map<String, String> headers = new HashMap<>();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
headers.put(headerName, request.getHeader(headerName));
}return headers; }}
public class ExceptionUtil {
/*** 異常列舉轉型別換為英文code
* @param error 異常列舉
* @return 大駝峰編碼*/
public static String errorToCodeEN(Enum<?> error) {
String errorName = error.name().toLowerCase();
String[] sp = errorName.split("_");
StringBuffer code = new StringBuffer();
for (String s : sp) {
code.append(StringUtils.capitalize(s));
}return code.toString();}}
public class AuthUtil {
public static boolean authUser(User user, HttpServletResponse respone)
throws Exception{
if(StringUtils.isEmpty(user.getUserId())) {
return false;}else {return true;}}}
> 觀察UserController類中的三個Controller層類提供介面,並且順序執行:
> eureka-server(伺服器)、zuul-server(伺服器) data-service(服務) user-service(服務)
> 成功後瀏覽器訪問http://localhost:9091/getContextUserId,
> 發現頁面空白,控制檯列印"the user is null,please access from gateway or chexk usr info."
> 說明攔截器起到作用,對於/沒有使用者資訊這樣不合法對請求進行了攔截。
> 程式碼:(UserContextInterceptor.java -定義的公共基礎包中) 類中所示。
public class UserContextInterceptor implements HandlerInterceptor {
private static final Logger log =
LoggerFactory.getLogger(UserContextInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse respone, Object arg2) throws Exception {
User user = new User(HttpConvertUtil.httpRequestToMap(request));
if(StringUtils.isEmpty(user.getUserId()) && StringUtils.isEmpty(user.getUserName())) {
log.error("the user is null, please access from gateway or check user info");
return false;}
UserContextHolder.set(user);return true;}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse respone,
Object arg2, ModelAndView arg3)
throws Exception {// DOING NOTHING}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse
respone, Object arg2, Exception arg3)
throws Exception { UserContextHolder.shutdown();}}
/**
* RestTemplate傳遞使用者上下文*/
public class RestTemplateUserContextInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution)
throws IOException {
User user = UserContextHolder.currentUser();
Map<String, String> headers = user.toHttpHeaders();
for (Map.Entry<String, String> header : headers.entrySet()) {
request.getHeaders().add(header.getKey(), header.getValue());}
// 呼叫
return execution.execute(request, body);}}
/*** Feign傳遞使用者上下文 */
public class FeignUserContextInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
// User user = UserContextHolder.currentUser();
ServletRequestAttributes attributes = (ServletRequestAttributes)
RequestContextHolder
.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
template.header(name, values);}}}}
/** * 通用異常資訊 */
public enum CommonError {
/*** 1001, "使用者資訊為空" */
AUTH_EMPTY_ERROR(10001, "the user is null, please check");
private Integer code;
private String message;
CommonError(Integer code, String message) {
this.code = code;this.message = message;}
public Integer getCode() {return code;}
public void setCode(Integer code) {
this.code = code;}
public String getMessage() {return message;}
public void setMessage(String message) {this.message = message;}
public String getCodeEn() {return ExceptionUtil.errorToCodeEN(this);}}
public class BaseExceptionBody implements Serializable {
/*** serialVersionUID*/
private static final long serialVersionUID = -1270478894426234738L;
/*** 相關業務ID */
private Long businessId;
/*** 異常編碼:數字*/
private Integer code;
/*** 異常編碼:英文短語*/
private String codeEN;
/*** 異常資訊*/
private String businessMessage;
/** * 異常型別*/
private String exceptionType;
public BaseExceptionBody(BaseException exception) {
this.businessId = exception.getBusinessId();
this.code = exception.getCode();
this.codeEN = exception.getCodeEN();
this.businessMessage = exception.getMessage();
this.exceptionType = exception.getClass().getName();}}
public class BaseException extends RuntimeException {
private static final long serialVersionUID = 1796731834836398434L;
private Long businessId;
private Integer code;
private String codeEN;
private String businessMessage;
/** * 基礎異常* * @param code 異常編碼:數字
* @param codeEN 異常編碼:英文短語 * @param message 異常資訊
* @param businessId 相關業務ID*/
public BaseException(Integer code, String codeEN, String message, Long businessId)
{ this(code, codeEN, message, businessId, null);}
public BaseException(Integer code, String codeEN, String message,
Long businessId, Throwable t) {
super(message, t);
this.businessId = businessId;
this.code = code;
this.codeEN = codeEN;
this.businessMessage = message;}}
/*** 使用者上下文*/
public class UserContextHolder {
public static ThreadLocal<User> context = new ThreadLocal<User>();
public static User currentUser() {return context.get();}
public static void set(User user) {context.set(user);}
public static void shutdown() {context.remove();}}
/*** Spring上下文管理工具 */
@Component
public class SpringContextManager implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
SpringContextManager.applicationContext = applicationContext;}
/*** 獲取上下文* * @return Spring上下文 */
public static ApplicationContext getApplicationContext() {
return applicationContext;}
/** * 獲取Spring配置 * @param key 配置名稱 * @return 配置值*/
public static String getProperties(String key) {
return applicationContext.getEnvironment().getProperty(key);}
/** * 獲取Spring配置<br> * 沒有配置時,返回預設值* @param key 配置名稱
* @param defaultValue 預設值 * @return 配置值*/
public static String getProperties(String key, String defaultValue) {
return applicationContext.getEnvironment().getProperty(key, defaultValue);}}
public class SpringCloudHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
private HystrixConcurrencyStrategy delegateHystrixConcurrencyStrategy;
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
return new HystrixThreadCallable<>(callable,
RequestContextHolder.getRequestAttributes(),
HystrixThreadLocal.threadLocal.get()); }
public SpringCloudHystrixConcurrencyStrategy() {init();}
private void init() {
try {
this.delegateHystrixConcurrencyStrategy =
HystrixPlugins.getInstance().getConcurrencyStrategy();
if (this.delegateHystrixConcurrencyStrategy instanceof
SpringCloudHystrixConcurrencyStrategy) { return;}
HystrixCommandExecutionHook commandExecutionHook =
HystrixPlugins.getInstance().getCommandExecutionHook();
HystrixEventNotifier eventNotifier =
HystrixPlugins.getInstance().getEventNotifier();
HystrixMetricsPublisher metricsPublisher =
HystrixPlugins.getInstance().getMetricsPublisher();
HystrixPropertiesStrategy propertiesStrategy =
HystrixPlugins.getInstance().getPropertiesStrategy();
HystrixPlugins.reset();
HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);}
catch (Exception e) {throw e;}}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixProperty<Integer> corePoolSize,
HystrixProperty<Integer> maximumPoolSize,
HystrixProperty<Integer> keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
return this.delegateHystrixConcurrencyStrategy.getThreadPool(
threadPoolKey, corePoolSize, maximumPoolSize,
keepAliveTime, unit, workQueue);}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixThreadPoolProperties threadPoolProperties) {
return this.delegateHystrixConcurrencyStrategy.getThreadPool(
threadPoolKey, threadPoolProperties); }
@Override
public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
return this.delegateHystrixConcurrencyStrategy.getBlockingQueue(maxQueueSize);}
@Override
public <T> HystrixRequestVariable<T> getRequestVariable(
HystrixRequestVariableLifecycle<T> rv) {
return this.delegateHystrixConcurrencyStrategy.getRequestVariable(rv);}}
public class HystrixThreadLocal {
public static ThreadLocal<String> threadLocal = new ThreadLocal<>();}
public class HystrixThreadCallable<S> implements Callable<S>{
private final RequestAttributes requestAttributes;
private final Callable<S> delegate;
private String params;
public HystrixThreadCallable(Callable<S> callable, RequestAttributes
requestAttributes,String params) {
this.delegate = callable;
this.requestAttributes = requestAttributes;
this.params = params; }
@Override
public S call() throws Exception {
try { RequestContextHolder.setRequestAttributes(requestAttributes);
HystrixThreadLocal.threadLocal.set(params);
return delegate.call(); } finally {
RequestContextHolder.resetRequestAttributes();
HystrixThreadLocal.threadLocal.remove();} } }
@Configuration
@EnableWebMvc
public class CommonConfiguration extends WebMvcConfigurerAdapter{
/*** 請求攔截器*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new UserContextInterceptor());}
/*** 建立Feign請求攔截器,在傳送請求前設定認證的使用者上下文資訊*/
@Bean
@ConditionalOnClass(Feign.class)
public FeignUserContextInterceptor feignTokenInterceptor() {
return new FeignUserContextInterceptor();}
/*** RestTemplate攔截器 * @return */
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add(new RestTemplateUserContextInterceptor());
return restTemplate;}
@Bean
public SpringCloudHystrixConcurrencyStrategy springCloudHystrixConcurrencyStrategy()
{return new SpringCloudHystrixConcurrencyStrategy();}}
> user-server | 9091| 使用者服務,對使用者資料的操作:
<dependency>
<groupId>cn.springcloud.book</groupId>
<artifactId>common</artifactId>
<version>${parent.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
eureka:
client:
serviceUrl:
defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8761}/eureka/
instance:
prefer-ip-address: true
management:
security:
enabled: false
endpoints:
web:
exposure:
include: hystrix.stream
feign:
hystrix:
enabled: true
ribbon:
ConnectTimeout: 6000
ReadTimeout: 6000
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 0
hystrix:
command:
default:
execution:
timeout:
isolation:
thread:
timeoutInMilliseconds: 15000
server:
port: 9091
spring:
cloud:
config:
label: master
uri: http://localhost:9090
name: config-info
profile: dev
application:
name: sc-user-service
public interface IUserService {
public String getDefaultUser();
public String getContextUserId();
public List<String> getProviderData();}
@Component
public class UserService implements IUserService{
@Autowired
private DataService dataService;
@Autowired
private RestTemplate restTemplate;
@Override
public String getDefaultUser() {return dataService.getDefaultUser();}
@Override
public String getContextUserId() {return dataService.getContextUserId();}
@Override
public List<String> getProviderData() {
List<String> result = restTemplate.getForObject
("http://sc-data-service/getProviderData", List.class);
return result;}}
@Component
public class UserClientFallback implements DataService{
@Override
public String getDefaultUser() {return new String("get getDefaultUser failed");}
@Override
public String getContextUserId() {return new String("get getContextUserId failed");}}
/*** feign呼叫資料服務*/
@FeignClient(name = "sc-data-service", fallback=UserClientFallback.class)
public interface DataService {
@RequestMapping(value = "/getDefaultUser", method = RequestMethod.GET)
public String getDefaultUser();
@RequestMapping(value = "/getContextUserId", method = RequestMethod.GET)
public String getContextUserId();}
@RestController
public class UserController {
@Autowired
private IUserService userService;
/*** 獲取配置檔案中系統預設使用者* @return*/
@GetMapping("/getDefaultUser")
public String getDefaultUser(){
return userService.getDefaultUser();}
/*** 獲取上下文使用者* @return */
@GetMapping("/getContextUserId")
public String getContextUserId(){
return userService.getContextUserId();}
/*** 獲取供應商資料*/
@GetMapping("/getProviderData")
public List<String> getProviderData(){
return userService.getProviderData();}}
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableCircuitBreaker
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args); }}
> data-server | 8099| API 資料服務,提供基礎的資料:
<dependency>
<groupId>cn.springcloud.book</groupId>
<artifactId>common</artifactId>
<version>${parent.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
spring:
application:
name: sc-data-service
eureka:
client:
serviceUrl:
defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8761}/eureka/
instance:
prefer-ip-address: true
management:
security:
enabled: false
endpoints:
web:
exposure:
include: hystrix.stream
server:
port: 8099
spring:
cloud:
config:
label: master
uri: http://localhost:9090
name: config-info
profile: dev
application:
name: sc-data-service
/*** @description : 配置資訊*/
@Component
@ConfigurationProperties(prefix = "遠端服務路徑")
public class DataConfig {
private String defaultUser;
public String getDefaultUser() {return defaultUser;}
public void setDefaultUser(String defaultUser) {
this.defaultUser = defaultUser;}}
@RestController
public class DataController {
@Autowired
private DataConfig dataConfig;
@GetMapping("/getContextUserId")
public String getContextUserId(){
return UserContextHolder.currentUser().getUserId();}
@GetMapping("/getDefaultUser")
public String getDefaultUser(){
return dataConfig.getDefaultUser();}
@GetMapping("/getProviderData")
public List<String> getProviderData(){
List<String> provider = new ArrayList<String>();
provider.add("Beijing Company");
provider.add("Shanghai Company");
provider.add("Shenzhen Company");
return provider;}}
/*** 資料服務* */
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class DataServiceApplication {
public static void main(String[] args) {
SpringApplication.run(DataServiceApplication.class, args);}}
=====================================================================================================
最後,給點進來的’碼’同志,提供一份參考配置生產環境,簡單模版供大家參考:
1) Eureka 服務端配置參考:
server:
port: 8761
spring:
appliction:
name: eurcka-server
eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:8761/eureka, http://127.0.0.1:8761/eureka
instance:
prefer-ip-address: true
server: enable-self-preser
vation: false
eviction-interval-timer-in-ms: 30000
Eureka屬性描述表格服務端:
2) Eureka 客戶端配置參考:
eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:8761/eureka, http://127.0.0.2:eureka/
instance:
prefer-ip-address: true
3) Ribbon 配置參考:
Ribbon的配置,一般都配置全域性的,也可以配置為單個服務的,這裡列了個全域性的。
–對於是否重試,在真實專案中觀察,由於冪等性或網路不穩定等原因導致易出問題,預設都不進行重試,
如果對於一些查詢比較多的服務可以開啟重試,根據具體專案來定義,
具體的配置可以看原始碼的 DefaultClientConfigImpl類.
ribbon:
ConnectTimeout: 2000 #全域性請求連線的超時時間,預設2秒
ReadTimeout: 5000 #全域性請求的超時時間,預設5秒
MaxAutoRetries: 0 #對當前例項的重試次數
MaxAutoRetriesNextServer: 0 #切換下一個例項重試次數
OkToRetryOnAllOperations: false #對所有操作請求都進行重試
4) Hystrix 配置參考:
Hystrix斷路器的配置,原始碼類 HystrixCommandProperties
通常全域性請求連線超時時間推薦設定為10秒,如果請求需要的時間過長則根據你的請求進行修改或
單獨時間,如果要設定執行緒池大小,可以看閘道器的配置如下:
hystrix.command.default.exection.isonlation.thread.timeoutlnMilliseconds
hystreix:
command:
default:
execution:
isolation:
thread:
timeoutlnMilliseconds: 10000 #全域性請求連線的超時時間預設為1秒
5) Zuul 閘道器 配置參考:
zuul:
ribbonlsolationStrategy: THREAD
threadPool:
userSeparateThreadPools: true
threadPoolKeyPrefix: zuulgateway
host:
max-per-route-connections: 50
max-total-connections: 300
socket-timeout-millis: 5000
connect-timeout-millis: 5000
hystrix:
threadpool:
default:
coreSize: 20
maximunmSize: 50
allowMaximumSizeToDivergeFromCoreSize: true
command:
default:
execution:
isolation:
thread:
timeoutlnMilliseconds: 10000
Zuul的屬性描述表:
相關文章
- 【夯實Spring Cloud】Spring Cloud中基於maven的分散式專案框架的搭建SpringCloudMaven分散式框架
- Spring Cloud基礎SpringCloud
- Spring Cloud基礎教程SpringCloud
- Spring Cloud微服務基礎元件實戰SpringCloud微服務元件
- Spring 框架基礎(01):核心元件總結,基礎環境搭建Spring框架元件
- ROS基礎入門——實操教程ROS
- Spring Cloud:Consul基礎知識SpringCloud
- 基於Spring Cloud搭建分散式配置中心SpringCloud分散式
- 後臺前端基礎框架搭建前端框架
- Spring Cloud Alibaba基礎教程:使用Sentinel實現介面限流SpringCloud
- Spring Cloud Alibaba微服務生態的基礎實踐SpringCloud微服務
- Spring Cloud雲架構-Restful 基礎架構SpringCloud架構REST
- 實現MVC基礎框架MVC框架
- Java基礎 --- 綜合練習Java
- spring cloud Alibaba 之 spring boot 基礎學習筆記CloudSpring Boot筆記
- Spring Cloud Alibaba | Sentinel: 服務限流基礎篇SpringCloud
- 整合spring cloud雲服務架構 - eureka 基礎SpringCloud架構
- JeeSite Spring Cloud安裝搭建SpringCloud
- spring cloud 服務搭建(1)SpringCloud
- MyBatis框架介紹及其實操MyBatis框架
- Spring Cloud+ Eureka微服務基礎專案搭建(已實現呼叫增刪改查微服務,持續更新)SpringCloud微服務
- flutter 案例 (一): 搭建介面基礎導航框架Flutter框架
- 【iOS 搭建基礎框架】目錄結構篇iOS框架
- Spring Cloud Alibaba基礎教程:Nacos的叢集部署SpringCloud
- 如何零基礎搭建一套微服務框架(Spring Boot + Dubbo + Docker + Jenkins)微服務框架Spring BootDockerJenkins
- Spring Boot基礎:Spring Boot簡介與快速搭建(1)Spring Boot
- TT建站之路--vue專案基礎框架搭建【01】Vue框架
- Spring review--Spring框架搭建SpringView框架
- Spring Cloud Alibaba 基礎教程:Nacos 生產級版本 0.8.0SpringCloud
- (十七)spring cloud微服務分散式雲架構-eureka 基礎SpringCloud微服務分散式架構
- Spring Cloud Alibaba基礎教程:Nacos的資料持久化SpringCloud持久化
- Spring Cloud Alibaba基礎教程:使用Nacos作為配置中心SpringCloud
- Spring Cloud Alibaba基礎教程:Nacos 生產級版本 0.8.0SpringCloud
- Spring Cloud構建分散式微服務雲架構基礎SpringCloud分散式微服務架構
- Spring Cloud Alibaba基礎教程:使用Nacos實現服務註冊與發現SpringCloud
- 基於Spring Boot和Spring Cloud實現微服務架構Spring BootCloud微服務架構
- Spring Cloud 快速入門(八)訊息系統整合框架 Spring Cloud StreamSpringCloud框架
- grails框架入門小結(一)—後臺基礎搭建AI框架