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)系统简称),高可用性集群,是保证业务连续性的有效解决方案,
-一般有两个或两个以上的节点,且分为活动节点及备用节点。

工程名端口描述
serverN/A父工程
config-server9090配置中心
eureka-server8761注册中心
zuul-server7777API GateWay 网关
hystrix-dashboard9099仪表盘& Turbine(风机)聚合Hystrix 断路器 -整合整个集群下的监控状态
commonN/A公共基础包,方便后台服务引用
user-server9091用户服务,对用户数据的操作
data-server8099API 数据服务,提供基础的数据
<!-父工程-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属性描述表格服务端:
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的属性描述表:
Zuul的属性描述表

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值