0%

字符串操作

字符串切片

字符串的第一个字符,编号为0,右邻的字符编号,一次增加1

取字符串切片的语法有两种

${变量:位置起点}

由指定的位置开始,截取子字符串到字符串结束

1
2
3
4
str="123456"
substr=${str:4}
# 打印结果为56
echo $substr

${变量:位置起点:长度}

1
2
3
4
str="123456"
substr=${str:2:5}
# 打印结果为3456
echo $substr
阅读全文 »

变量和引号

在shell中设定变量使用单引号和双引号有什么区别呢?

双引号

双引号的功能比较强大

  • 替换变量

    1
    2
    3
    myname="Bash"
    HelloWorld="你好,我是 $myname"
    echo $HelloWorld

    输出结果为 你好,我是 Bash

    如果使用单引号则不行

  • 替换命令执行结果

  • 替换算数运算结果

单引号

单引号的作用就是形成一个字符串,写什么就是什么

Feign拦截器

通过实现RequestInterceptor接口来实现Feign的拦截器,实现apply方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class FeignRequestInterceptor implements RequestInterceptor
{
@Override
public void apply(RequestTemplate requestTemplate)
{
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();

if (StringUtils.isNotNull(httpServletRequest))
{
Map<String, String> headers = ServletUtils.getHeaders(httpServletRequest);
// 传递用户信息请求头,防止丢失
String userId = headers.get("userId");
if (StringUtils.isNotEmpty(userId))
{
requestTemplate.header("userId", userId);
}
String userName = headers.get("userName");
if (StringUtils.isNotEmpty(userName))
{
requestTemplate.header("userName", userName);
}
String authentication = headers.get("authentication");
if (StringUtils.isNotEmpty(authentication))
{
requestTemplate.header("authentication", authentication);
}

// 配置客户端IP
requestTemplate.header("X-Forwarded-For", IpUtils.getIpAddr(ServletUtils.getRequest()));
}
}
}

Feign的执行流程

  • 首先通过@EnableFeignClients注解开启Feign功能,程序启动时开启对@FeignClient注解的扫描

    1
    2
    3
    4
    5
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(FeignClientsRegistrar.class)
    public @interface EnableFeignClients
  • 当接口的方法调用时,通过动态代理SynchronousMethodHandler生成具体的RequestTemplate

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    Options options = findOptions(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
    try {
    return executeAndDecode(template, options);
    } catch (RetryableException e) {
    try {
    retryer.continueOrPropagate(e);
    } catch (RetryableException th) {
    Throwable cause = th.getCause();
    if (propagationPolicy == UNWRAP && cause != null) {
    throw cause;
    } else {
    throw th;
    }
    }
    if (logLevel != Logger.Level.NONE) {
    logger.logRetry(metadata.configKey(), logLevel);
    }
    continue;
    }
    }
    }
1
RequestTemplate template = buildTemplateFromArgs.create(argv);

在构建RequestTemplate的时候会进行参数解析,如果参数是@RequestBody的会调用Encoder的encode方法进行编码(BuildEncodedTemplateFromArgs)

1
encoder.encode(body, metadata.bodyType(), mutable);
  • 再根据RequestTemplate生成HTTP的Request对象

  • Request对象交给Client处理,可配置HttpURLConnection、HttpClient、OkHttp等

    1
    2
    3
    // 该方法首先会调用RequestInterceptor的apply方法处理请求,之后才会进行请求发送
    // 执行完之后调用Decoder的decode方法来进行解码
    return executeAndDecode(template, options);
  • Client被封装到LoadBalanceClient类进行负载均衡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
// 调用RequestInterceptor的apply方法处理请求
Request request = targetRequest(template);

if (logLevel != Logger.Level.NONE) {
logger.logRequest(metadata.configKey(), logLevel, request);
}

Response response;
long start = System.nanoTime();
try {
// 请求发送
response = client.execute(request, options);
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
}
throw errorExecuting(request, e);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

boolean shouldClose = true;
try {
if (logLevel != Logger.Level.NONE) {
response =
logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
}
if (Response.class == metadata.returnType()) {
if (response.body() == null) {
return response;
}
if (response.body().length() == null ||
response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
shouldClose = false;
return response;
}
// Ensure the response body is disconnected
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
return response.toBuilder().body(bodyData).build();
}
if (response.status() >= 200 && response.status() < 300) {
if (void.class == metadata.returnType()) {
return null;
} else {
//调用Decoder的decode方法来进行解码
Object result = decode(response);
shouldClose = closeAfterDecode;
return result;
}
} else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
Object result = decode(response);
shouldClose = closeAfterDecode;
return result;
} else {
// 调用ErrorDecoder进行错误解码
throw errorDecoder.decode(metadata.configKey(), response);
}
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
}
throw errorReading(request, response, e);
} finally {
if (shouldClose) {
ensureClosed(response.body());
}
}
}

系统日志

messages日志

linux有一个很重要的日志文件/var/log/messages,是核心系统文件,包含了系统启动时的引导消息,以及系统运行时的其他状态消息。IO错误、网络错误和其他系统错误都会记录到这个文件中

可以用该日志判断进程是否被Killer杀死掉。

在文件里查找下,是否有之前pid对应的进程Kill信息,或者进程名,比如我们这里说的是Java应用,就直接查Java的就可以,像这样的内容,

“Out of memory: Kill process 31201 (java) score 783 or sacrifice child

Linux Kernel的这个Killer,会在内存不足的时候kill掉任何不受保护的进程,从而释放内存,保证Kernel的运行

echo -17 > /proc/$PID/oom_adj 使得自己的进程受保护

dmesg日志

/var/log/dmesg日志中记录了系统的启动信息,可以使用dmesg命令来查看,如果某个硬件有问题,使用这个命令是可以看到的

1
dmesg | grep error
阅读全文 »