Spring cloud学习总结

摘要:
1注册中心学习maven配置org.springframework.cloudspring-cloud-starter-netflix-eureka-clientorg.springframework.cloud˂/gr

1 注册中心学习

maven配置

        <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-eureka-server</artifactId>
        </dependency>

2 配置

spring.application.name=spring-cloud-eureka

server.port=8000
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/

3 访问地址:

http://localhost:8000/

4 eclipse也有自动装配的 spring cloud项目了

Spring cloud学习总结第1张

3 okhttp3 的使用可以替代 apache 的httpclient,很好用,两个例子

get和post的方法

应该是spring cloud封装了okhttp3 依赖是
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-core</artifactId>
<version>2.0.0.M1</version>
<scope>provided</scope>
</dependency>

原生的依赖是:
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.0.1</version>
</dependency>
这个是最新版的

下面是两个例子
@Test
public void testGet(){
//创建OkHttpClient实例对象
OkHttpClient okHttpClient = new OkHttpClient();
//创建Request对象
Request request = new Request.Builder()
.url("https://www.httpbin.org/get?id=111")
.addHeader("key","value")
.get()
.build();
//执行Request请求
//异步请求
/*okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//请求失败
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//请求成功
Log.d("TestOkHttp",response.body().string());
}
});*/
//同步请求
try {
Response response = okHttpClient.newCall(request).execute();
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}

@Test
public void testPost(){
//1、创建OkHttpClient对象实例
OkHttpClient okHttpClient = new OkHttpClient();
//2、创建Request对象
MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
RequestBody requestBody = RequestBody.create(mediaType,"{}");
Request request = new Request.Builder()
.url("https://www.httpbin.org/post")
.post(requestBody)
.build();
//3、执行Request请求
try {
Response response = okHttpClient.newCall(request).execute();
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}

4okhttp3 的自己的拦截器,请求前设置设置token等值。。。如果返回时过期的则重新刷新后再次重新生成

import com.github.wxiaoqi.security.auth.client.config.ServiceAuthConfig;
import com.github.wxiaoqi.security.auth.client.config.UserAuthConfig;
import com.github.wxiaoqi.security.auth.client.jwt.ServiceAuthUtil;
import com.github.wxiaoqi.security.common.constant.CommonConstants;
import com.github.wxiaoqi.security.common.context.BaseContextHandler;
import lombok.extern.java.Log;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
* @author ace
*/
@Component
@Log
public class OkHttpTokenInterceptor implements Interceptor {
@Autowired
@Lazy
private ServiceAuthUtil serviceAuthUtil;
@Autowired
@Lazy
private ServiceAuthConfig serviceAuthConfig;
@Autowired
@Lazy
private UserAuthConfig userAuthConfig;

@Override
public Response intercept(Chain chain) throws IOException {
Request newRequest = null;
if (chain.request().url().toString().contains("client/token")) {
newRequest = chain.request()
.newBuilder()
.header(userAuthConfig.getTokenHeader(), BaseContextHandler.getToken())
.build();
} else {
newRequest = chain.request()
.newBuilder()
.header(userAuthConfig.getTokenHeader(), BaseContextHandler.getToken())
.header(serviceAuthConfig.getTokenHeader(), serviceAuthUtil.getClientToken())
.build();
}
Response response = chain.proceed(newRequest);
if (HttpStatus.FORBIDDEN.value() == response.code()) {
if (response.body().string().contains(String.valueOf(CommonConstants.EX_CLIENT_INVALID_CODE))) {
log.info("Client Token Expire,Retry to request...");
serviceAuthUtil.refreshClientToken();
newRequest = chain.request()
.newBuilder()
.header(userAuthConfig.getTokenHeader(), BaseContextHandler.getToken())
.header(serviceAuthConfig.getTokenHeader(), serviceAuthUtil.getClientToken())
.build();
response = chain.proceed(newRequest);
}
}
return response;
}
}

保存修改成功

5 使用 feign 做的 请求负载均衡

使用了接口和服务名
import com.github.wxiaoqi.security.common.msg.ObjectRestResponse;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

/**
* Created by ace on 2017/9/15.
*/
@FeignClient(value = "${auth.serviceId}",configuration = {})
public interface ServiceAuthFeign {
@RequestMapping(value = "/client/myClient")
public ObjectRestResponse<List<String>> getAllowedClient(@RequestParam("serviceId") String serviceId, @RequestParam("secret") String secret);
@RequestMapping(value = "/client/token",method = RequestMethod.POST)
public ObjectRestResponse getAccessToken(@RequestParam("clientId") String clientId, @RequestParam("secret") String secret);
@RequestMapping(value = "/client/servicePubKey",method = RequestMethod.POST)
public ObjectRestResponse<byte[]> getServicePublicKey(@RequestParam("clientId") String clientId, @RequestParam("secret") String secret);
@RequestMapping(value = "/client/userPubKey",method = RequestMethod.POST)
public ObjectRestResponse<byte[]> getUserPublicKey(@RequestParam("clientId") String clientId, @RequestParam("secret") String secret);

}

6 spring 配置拦截器的第二种方式,用类继承的

import com.github.wxiaoqi.security.auth.client.annotation.IgnoreClientToken;
import com.github.wxiaoqi.security.auth.client.config.ServiceAuthConfig;
import com.github.wxiaoqi.security.auth.client.jwt.ServiceAuthUtil;
import com.github.wxiaoqi.security.auth.common.util.jwt.IJWTInfo;
import com.github.wxiaoqi.security.common.exception.auth.ClientForbiddenException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
* Created by ace on 2017/9/12.
*/
@SuppressWarnings("ALL")
public class ServiceAuthRestInterceptor extends HandlerInterceptorAdapter {
private Logger logger = LoggerFactory.getLogger(ServiceAuthRestInterceptor.class);

@Autowired
private ServiceAuthUtil serviceAuthUtil;

@Autowired
private ServiceAuthConfig serviceAuthConfig;

private List<String> allowedClient;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 配置该注解,说明不进行服务拦截
IgnoreClientToken annotation = handlerMethod.getBeanType().getAnnotation(IgnoreClientToken.class);
if (annotation == null) {
annotation = handlerMethod.getMethodAnnotation(IgnoreClientToken.class);
}
if(annotation!=null) {
return super.preHandle(request, response, handler);
}

String token = request.getHeader(serviceAuthConfig.getTokenHeader());
IJWTInfo infoFromToken = serviceAuthUtil.getInfoFromToken(token);
String uniqueName = infoFromToken.getUniqueName();
for(String client:serviceAuthUtil.getAllowedClient()){
if(client.equals(uniqueName)){
return super.preHandle(request, response, handler);
}
}
throw new ClientForbiddenException("Client is Forbidden!");
}
}

7 配置全局异常的方法@ControllerAdvice

import com.github.wxiaoqi.security.common.constant.CommonConstants;
import com.github.wxiaoqi.security.common.exception.BaseException;
import com.github.wxiaoqi.security.common.exception.auth.ClientTokenException;
import com.github.wxiaoqi.security.common.exception.auth.UserInvalidException;
import com.github.wxiaoqi.security.common.exception.auth.UserTokenException;
import com.github.wxiaoqi.security.common.msg.BaseResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletResponse;

/**
* Created by ace on 2017/9/8.
*/
@ControllerAdvice("com.github.wxiaoqi.security")
@ResponseBody
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

@ExceptionHandler(ClientTokenException.class)
public BaseResponse clientTokenExceptionHandler(HttpServletResponse response, ClientTokenException ex) {
response.setStatus(403);
logger.error(ex.getMessage(),ex);
return new BaseResponse(ex.getStatus(), ex.getMessage());
}

@ExceptionHandler(UserTokenException.class)
public BaseResponse userTokenExceptionHandler(HttpServletResponse response, UserTokenException ex) {
response.setStatus(200);
logger.error(ex.getMessage(),ex);
return new BaseResponse(ex.getStatus(), ex.getMessage());
}

@ExceptionHandler(UserInvalidException.class)
public BaseResponse userInvalidExceptionHandler(HttpServletResponse response, UserInvalidException ex) {
response.setStatus(200);
logger.error(ex.getMessage(),ex);
return new BaseResponse(ex.getStatus(), ex.getMessage());
}

@ExceptionHandler(BaseException.class)
public BaseResponse baseExceptionHandler(HttpServletResponse response, BaseException ex) {
logger.error(ex.getMessage(),ex);
response.setStatus(500);
return new BaseResponse(ex.getStatus(), ex.getMessage());
}

@ExceptionHandler(Exception.class)
public BaseResponse otherExceptionHandler(HttpServletResponse response, Exception ex) {
response.setStatus(500);
logger.error(ex.getMessage(),ex);
return new BaseResponse(CommonConstants.EX_OTHER_CODE, ex.getMessage());
}

}

8 获取客户ip的真实ip的三种方法

public class ClientUtil {
/**
* 获取客户端真实ip
* @param request
* @return
*/
public static String getClientIp(HttpServletRequest request){
String ip = request.getHeader("x-forwarded-for");
if (ip==null||ip.length()==0||"unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip==null||ip.length()==0||"unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip==null||ip.length()==0||"unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}

9 反射强制直线set get 设置值,获取值,忽略 private protected 的修饰符,

package com.github.wxiaoqi.security.common.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

/**
* 反射工具类.
* 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
* @author calvin
* @version 2013-01-15
*/
@SuppressWarnings("rawtypes")
public class ReflectionUtils {
private static final String SETTER_PREFIX = "set";

private static final String GETTER_PREFIX = "get";

private static final String CGLIB_CLASS_SEPARATOR = "$$";
private static Logger logger = LoggerFactory.getLogger(ReflectionUtils.class);

/**
* 调用Getter方法.
* 支持多级,如:对象名.对象名.方法
*/
public static Object invokeGetter(Object obj, String propertyName) {
Object object = obj;
for (String name : StringUtils.split(propertyName, ".")){
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
}
return object;
}

/**
* 调用Setter方法, 仅匹配方法名。
* 支持多级,如:对象名.对象名.方法
*/
public static void invokeSetter(Object obj, String propertyName, Object value) {
Object object = obj;
String[] names = StringUtils.split(propertyName, ".");
for (int i=0; i<names.length; i++){
if(i<names.length-1){
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
}else{
String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
invokeMethodByName(object, setterMethodName, new Object[] { value });
}
}
}

/**
* 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
*/
public static Object getFieldValue(final Object obj, final String fieldName) {
Field field = getAccessibleField(obj, fieldName);

if (field == null) {
throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
}

Object result = null;
try {
result = field.get(obj);
} catch (IllegalAccessException e) {
logger.error("不可能抛出的异常{}", e.getMessage());
}
return result;
}

/**
* 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
*/
public static void setFieldValue(final Object obj, final String fieldName, final Object value) {
Field field = getAccessibleField(obj, fieldName);

if (field == null) {
logger.error("Could not find field [" + fieldName + "] on target [" + obj + "]");
return;
//throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
}
try {
field.set(obj, convert(value, field.getType()));
} catch (IllegalAccessException e) {
logger.error("不可能抛出的异常:{}", e.getMessage());
}
}
public static Object convert(Object object, Class<?> type) {
if (object instanceof Number) {
Number number = (Number) object;
if (type.equals(byte.class) || type.equals(Byte.class)) {
return number.byteValue();
}
if (type.equals(short.class) || type.equals(Short.class)) {
return number.shortValue();
}
if (type.equals(int.class) || type.equals(Integer.class)) {
return number.intValue();
}
if (type.equals(long.class) || type.equals(Long.class)) {
return number.longValue();
}
if (type.equals(float.class) || type.equals(Float.class)) {
return number.floatValue();
}
if (type.equals(double.class) || type.equals(Double.class)) {
return number.doubleValue();
}
}
if(type.equals(String.class)){
return object==null?"":object.toString();
}
return object;
}

/**
* 直接调用对象方法, 无视private/protected修饰符.
* 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
* 同时匹配方法名+参数类型,
*/
public static Object invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
final Object[] args) {
Method method = getAccessibleMethod(obj, methodName, parameterTypes);
if (method == null) {
throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
}

try {
return method.invoke(obj, args);
} catch (Exception e) {
throw convertReflectionExceptionToUnchecked(e);
}
}

/**
* 直接调用对象方法, 无视private/protected修饰符,
* 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
* 只匹配函数名,如果有多个同名函数调用第一个。
*/
public static Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) {
Method method = getAccessibleMethodByName(obj, methodName);
if (method == null) {
throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
}

try {
return method.invoke(obj, args);
} catch (Exception e) {
throw convertReflectionExceptionToUnchecked(e);
}
}

/**
* 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
*
* 如向上转型到Object仍无法找到, 返回null.
*/
public static Field getAccessibleField(final Object obj, final String fieldName) {
Validate.notNull(obj, "object can't be null");
Validate.notBlank(fieldName, "fieldName can't be blank");
for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
try {
Field field = superClass.getDeclaredField(fieldName);
makeAccessible(field);
return field;
} catch (NoSuchFieldException e) {//NOSONAR
// Field不在当前类定义,继续向上转型
continue;// new add
}
}
return null;
}

/**
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
* 如向上转型到Object仍无法找到, 返回null.
* 匹配函数名+参数类型。
*
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
*/
public static Method getAccessibleMethod(final Object obj, final String methodName,
final Class<?>... parameterTypes) {
Validate.notNull(obj, "object can't be null");
Validate.notBlank(methodName, "methodName can't be blank");

for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
try {
Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
makeAccessible(method);
return method;
} catch (NoSuchMethodException e) {
// Method不在当前类定义,继续向上转型
continue;// new add
}
}
return null;
}

/**
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
* 如向上转型到Object仍无法找到, 返回null.
* 只匹配函数名。
*
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
*/
public static Method getAccessibleMethodByName(final Object obj, final String methodName) {
Validate.notNull(obj, "object can't be null");
Validate.notBlank(methodName, "methodName can't be blank");

for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
Method[] methods = searchType.getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals(methodName)) {
makeAccessible(method);
return method;
}
}
}
return null;
}

/**
* 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
*/
public static void makeAccessible(Method method) {
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
&& !method.isAccessible()) {
method.setAccessible(true);
}
}

/**
* 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
*/
public static void makeAccessible(Field field) {
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier
.isFinal(field.getModifiers())) && !field.isAccessible()) {
field.setAccessible(true);
}
}

/**
* 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
* 如无法找到, 返回Object.class.
* eg.
* public UserDao extends HibernateDao<User>
*
* @param clazz The class to introspect
* @return the first generic declaration, or Object.class if cannot be determined
*/
@SuppressWarnings("unchecked")
public static <T> Class<T> getClassGenricType(final Class clazz) {
return getClassGenricType(clazz, 0);
}

/**
* 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
* 如无法找到, 返回Object.class.
*
* 如public UserDao extends HibernateDao<User,Long>
*
* @param clazz clazz The class to introspect
* @param index the Index of the generic ddeclaration,start from 0.
* @return the index generic declaration, or Object.class if cannot be determined
*/
public static Class getClassGenricType(final Class clazz, final int index) {

Type genType = clazz.getGenericSuperclass();

if (!(genType instanceof ParameterizedType)) {
logger.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
return Object.class;
}

Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

if (index >= params.length || index < 0) {
logger.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
+ params.length);
return Object.class;
}
if (!(params[index] instanceof Class)) {
logger.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
return Object.class;
}

return (Class) params[index];
}
public static Class<?> getUserClass(Object instance) {
Assert.notNull(instance, "Instance must not be null");
Class clazz = instance.getClass();
if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
Class<?> superClass = clazz.getSuperclass();
if (superClass != null && !Object.class.equals(superClass)) {
return superClass;
}
}
return clazz;

}
/**
* 将反射时的checked exception转换为unchecked exception.
*/
public static RuntimeException convertReflectionExceptionToUnchecked(Exception e) {
if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
|| e instanceof NoSuchMethodException) {
return new IllegalArgumentException(e);
} else if (e instanceof InvocationTargetException) {
return new RuntimeException(((InvocationTargetException) e).getTargetException());
} else if (e instanceof RuntimeException) {
return (RuntimeException) e;
}
return new RuntimeException("Unexpected Checked Exception.", e);
}
/**
* 判断某个对象是否拥有某个属性
*
* @param obj 对象
* @param fieldName 属性名
* @return 有属性返回true
* 无属性返回false
*/
public static boolean hasField(final Object obj, final String fieldName){
Field field = getAccessibleField(obj, fieldName);
if (field == null) {
return false;
}
return true;

}
}

3 sleuth跟踪器

1 spring cloud sleuth 服务跟踪系统
使用具体流程,server, 调用trace1 会调用trace2,搭建这三个,
开始配置跟踪,1 先在trace1和2添加 sleuth的 maven依赖
2 请求返回四个字段【a,b,c,d】 a表示 trace1名字,b表示tracieid 一个链路一个 id,c表示 spanid,就是每个小段的id,d是truefalse,
默认false,表示sleuth不收集数据

2 跟踪器需要和 zipkin整合

zipkin是 推特开源的 分布式链路系统,优点是有个好看的ui界面,包括了server名字等等

要改 的话如果不整合 spring boot,只需要两步

1 添加 java包,第二 配置端口和server name ,第三是启动 enablezipkin, 然后浏览器浏览器就可以看到主界面了

Spring cloud学习总结第2张

localhost:port/ 就是首页

3 Zuul的学习

参考 文档https://www.cnblogs.com/ityouknow/p/6944096.html
1Zuul 
添加网关为了统一来处理 请求接口
1 添加依赖 引入 spring-cloud-starter-zuul
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
2 属性文件, 请求 /it/xxx直接跳转到 ityoukneowxxx,类似于代理
spring.application.name=gateway-service-zuul
server.port=8888
#这里的配置表示,访问/it/** 直接重定向到http://www.ityouknow.com/**
zuul.routes.baidu.path=/it/**
zuul.routes.baidu.url=http://www.ityouknow.com/
3 启动类 这样请求会自动重定向
@SpringBootApplication
@EnableZuulProxy
public class GatewayServiceZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(GatewayServiceZuulApplication.class, args);
    }
}
4 更高级的如果新添加了,或者动态不指定 路径则无法实现了,后来
思路是 用服务会euroke
1 添加 euroke
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
2 修改属性
spring.application.name=gateway-service-zuul
server.port=8888

zuul.routes.api-a.path=/producer/**
zuul.routes.api-a.serviceId=spring-cloud-producer

eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/
如果配置了集群,则会自动 负载均衡,在请求转发的情况下,这样就转发到了集群

nginx和 zuul功能差不多

4 Hystrix

1hystrix
随意实施的熔断对整个系统的影响是灾难性的
雪崩效应
一个错导致级联错误,所以需要  熔断,像保险丝一样
 错误产生于调用服务端,所以只在客户端加熔断就可以了
 1属性文件加上
 feign.hystrix.enabled=true
 2建立回调类
 @Component
public classHelloRemoteHystrix implements HelloRemote{

    @Override
    public String hello(@RequestParam(value = "name") String name) {
        return "hello" +name+", this messge send failed ";
    }
}

3添加调用错误的回调 fallback
@FeignClient(name= "spring-cloud-producer",fallback = HelloRemoteHystrix.class)
public interfaceHelloRemote {

    @RequestMapping(value = "/hello")
    public String hello(@RequestParam(value = "name") String name);

}
4 测试步骤是启动服务和客户端,调用正常,关闭服务端,调用仍然返回数据的

免责声明:文章转载自《Spring cloud学习总结》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Node.js Express博客项目实战 之 项目需求分析李洪强iOS开发之基于彻底解耦合的实验性iOS架构下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

SpringBoot2.0+SpringCloud Eureka搭建高可用注册中心(Eureka之二)

上一篇中提到用SpringBoot2.0+Eureka搭建服务注册中心和服务提供者,详情参考: https://www.cnblogs.com/SysoCjs/p/10127448.html         现在讲一下SpringCloud+Eureka搭建高可用注册中心。在微服务架构的分布式环境中,必须充分考虑可能会发生的故障情况,所以生产环境必须对各个...

根据PID查看具体的容器

根据节点具体的PID如何定位到指定的容器,三种方法 笨方法,根据进程PID定位,如节点运行了很多的java进程,在部署设计时没有什么区别及环境变量,很难区分进程对应的容器 列出具体应用的进程,如下 <root@SIT-K8S-WN7 ~># ps -ef | grep java nflow 1272 1245 9 11:26 ?...

Dubbo 3.0 前瞻:重塑 Spring Cloud 服务治理

作者 | 小马哥 导读:Dubbo 社区策划了【Dubbo 云原生之路】系列文章,和大家一起回顾 Apache Dubbo 产品和社区的发展,并展望未来发展。系列文章主要涵盖 Dubbo 技术解读、社区运营、应用案例解析三大部分。本文为系列第 3 篇。 前言 在 Java 微服务生态中,Spring Cloud 成为了开发人员的首选技术栈,然而随着实践的...

详解Eureka 缓存机制

原文:https://www.cnblogs.com/yixinjishu/p/10871243.html 引言 Eureka是Netflix开源的、用于实现服务注册和发现的服务。Spring Cloud Eureka基于Eureka进行二次封装,增加了更人性化的UI,使用更为方便。但是由于Eureka本身存在较多缓存,服务状态更新滞后,最常见的状况是:服...

SpringCloud2.0 Zuul 网关路由 基础教程(十)

1、启动基础工程 1.1、启动【服务注册中心】,工程名称:springcloud-eureka-server 参考 SpringCloud2.0 Eureka Server 服务中心 基础教程(二) 1.2、启动【服务提供者】,工程名称:springcloud-eureka-client 参考 SpringCloud2.0 Eureka Client 服务...

理论:第六章:SpringCould组件有哪些,他们的作用是什么(说七八个)?微服务的CAP是什么?BASE是什么?

先讲五大核心组件,(偷个懒,嘻嘻)这里我引用一位大佬讲解的,原文地址是:https://juejin.im/post/5be13b83f265da6116393fc7 一、业务场景介绍 先来给大家说一个业务场景,假设咱们现在开发一个电商网站,要实现支付订单的功能,流程如下: 创建一个订单后,如果用户立刻支付了这个订单,我们需要将订单状态更新为“已支付”...