Spring Boot / Cloud 框架相关

一、 Spring Cloud分布式开发五大组件

  • 服务发现——Netflix Eureka、SpringCloud Alibaba Nacos Discovery
  • 客户端负载均衡——Netflix Ribbon
  • 断路器——Netflix Hystrix、SpringCloud Alibaba Sentinel
  • 服务网关——Netflix Zuul、Spring Cloud Gateway
  • 分布式配置——Spring Cloud Config、SpringCloud Alibaba Nacos Config

微服务核心组件
spring & Netflix其他其他
服务发现与注册Netflix Eureka(client、server)nacos
客户端负载均衡Netflix Ribbon
断路器Netflix Hystrix
服务网关Netflix Zuul Gateway
分布式配置Spring Cloud Configalibaba-nacos-configapollo
分布式注册中心Spring Cloud Serveralibaba-nacos-discovery

二、服务调用

1.Feign调用

(1)传递非业务数据

当微服务之间互相调用的时候,需要传递非业务数据的时候,可以使用拦截器实现,具体实现方式是实现feign.RequestInterceptor接口,实现具体的apply()方法逻辑。

package com.irootech.useraccess.config;

import com.irootech.common.util.SessionBean;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;


@Configuration
public class FeignRequestInterceptor implements RequestInterceptor {


    @Override
    public void apply(RequestTemplate requestTemplate) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();

        // 设置本地变量
        requestTemplate.header("tenantId", SessionBean.getTenantId());
        requestTemplate.header("userId", SessionBean.getUserId());
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            Enumeration<String> headerNames = request.getHeaderNames();
            if (headerNames != null) {
                while (headerNames.hasMoreElements()) {
                    String name = headerNames.nextElement();
                    if (requestTemplate.headers().containsKey(name)) {
                        continue;
                    }
                    if (name.equals("content-length") || (name.equalsIgnoreCase("content-type"))) {//此处是个坑,会把现有的content-length去调用feigin接口,报错
                        continue;
                    }
                    String values = request.getHeader(name);
                    requestTemplate.header(name, values);
                }
            }
        }
    }
}
(2)错误和异常数据解析

实现  feign.codec.ErrorDecoder 接口

package com.irootech.useraccess.config;

import com.irootech.common.exception.SysException;
import com.irootech.common.util.JsonParsingUtil;
import com.irootech.useraccess.feign.vo.ACLBaseResponse;
import com.irootech.useraccess.feign.vo.ACLErrorMessage;
import feign.Response;
import feign.Util;
import feign.codec.ErrorDecoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.util.List;

/**
 * @Author: fei.yu
 * @Date: 2021/2/1 10:20
 */
@Configuration
@Slf4j
public class FeignClientErrorDecoder implements ErrorDecoder {

    @Override
    public Exception decode(String methodKey, Response response) {
        log.info("feign client response:", response);
        UserAccessResultCode userAccessResultCode = UserAccessResultCode.getByCode(response.status());
        if (userAccessResultCode != UserAccessResultCode.ERROR) {
            return new SysException(userAccessResultCode);
        }

        RemoteExceptionEnumCode remoteExceptionCode = null;
        if (response.body() == null) {
            log.error("feign调用异常,body为空");
//            remoteExceptionCode = new RemoteExceptionCode(response.status(),"", "");
            remoteExceptionCode = RemoteExceptionEnumCode.getByMessageEN("");
        }else if(response.headers().containsKey("x-rootcloud-setpw-token")){
            List<String> setPwTokenParam = (List<String>)response.headers().get("x-rootcloud-setpw-token");
            SysException sysException = new SysException(UserAccessResultCode.getByCode(8610),setPwTokenParam.get(0));
            throw sysException;
        } else {
            try {
                String body = Util.toString(response.body().asReader());
                log.error("feign调用异常,body为 ==>{}",body);
                ACLBaseResponse baseResponse = JsonParsingUtil.fromJson(body, ACLBaseResponse.class);
                try {
                    ACLErrorMessage errorMessage = JsonParsingUtil.fromJson(baseResponse.getMessage(), ACLErrorMessage.class);
//                    remoteExceptionCode = new RemoteExceptionCode(baseResponse.getStatus(),
//                            errorMessage.getMessage(), errorMessage.getMessage());
                    remoteExceptionCode = RemoteExceptionEnumCode.getByMessageEN(errorMessage.getMessage());
                } catch (Exception e) {
//                    remoteExceptionCode = new RemoteExceptionCode(baseResponse.getStatus(),
//                            baseResponse.getMessage(), baseResponse.getMessage());
                    remoteExceptionCode = RemoteExceptionEnumCode.getByMessageEN(baseResponse.getMessage());
                }

            } catch (IOException e) {
                log.error("feign.IOException", e);
                throw new SysException(userAccessResultCode);
            }
        }

        return new SysException(remoteExceptionCode);
    }

}

三、服务启动与加载

1. Springboot读取配置文件原理和加载顺序优先级 :

(1)读取配置文件原理

Springboot读取配置文件是通过事件监听的方式读取的,在Springboot启动的时候,会发布一个ApplicationEnvironmentPreparedEvent事件,ConfigFileApplicationListener监听器监听了这个事件,在该监听器中读取配置文件。

通过事件监听的方式读取的配置文件,这个监听器是ConfigFileApplicationListener。

(2)配置文件加载顺序:

(开发环境下)

使用SpringCloudConfig这种统一配置时Spring Boot 配置文件的加载顺序,依次为bootstrap.properties-> bootstrap.yml ->application.properties-> application.yml,其中bootstrap.properties配置为最高优先级。

(部署环境下)

下面目录下的配置文件的优先级从高到低,高优先级的配置覆盖低优先级的配置,所有配置会形成互补配置。

# jar文件同级目录下的config文件夹下的配置文件file:./config/  

# jar文件同级目录下的配置文件file:./

# classpath目录下的config文件夹下的配置文件classpath:config/

# classpath目录下的配置文件classpath

2.项目启动初始化操作实现方案

项目启动的时候,需要加载字典数据到redis里面,除了单例代码块初始化实现之外,还可以使用@PostConstruct注解实现。在service实现类对应的方法上面加这个注解,Spring容器就会在依赖注入之后调用此方法。

// 可以看到,它是通过注解@PostConstruct实现的自动加载数据,带有该注解的方法会在依赖注入之后调用

    /**
     * 项目启动时,初始化字典到缓存
     */
    @PostConstruct
    public void init()
    {
        loadingDictCache();
    }

3.不需要连接数据库的微服务启动

例如Gateway 服务,需要在启动类加排除数据库自动注册的类

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })

/**
 * 网关启动程序
 * 
 * @author ruoyi
 */
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
public class RuoYiGatewayApplication
{
    public static void main(String[] args)
    {
        SpringApplication.run(RuoYiGatewayApplication.class, args);
        System.out.println("(♥◠‿◠)ノ゙  若依网关启动成功   ლ(´ڡ`ლ)゙  \n" +
                " .-------.       ____     __        \n" +
                " |  _ _   \\      \\   \\   /  /    \n" +
                " | ( ' )  |       \\  _. /  '       \n" +
                " |(_ o _) /        _( )_ .'         \n" +
                " | (_,_).' __  ___(_ o _)'          \n" +
                " |  |\\ \\  |  ||   |(_,_)'         \n" +
                " |  | \\ `'   /|   `-'  /           \n" +
                " |  |  \\    /  \\      /           \n" +
                " ''-'   `'-'    `-..-'              ");
    }
}

四、windows启动java服务

1. 方式一:java -jar xxx.jar

2. 方式二:直接进程运行,可关闭窗口

(1)定义startup.bat文件一

这种方式需要手动关闭窗口

@echo off
@javaw -jar  ruoyi-admin.jar > ruoyi-admin.log
exit

(2)定义startup.bat文件二

@echo off
start javaw -jar  ruoyi-admin.jar > ruoyi-admin.log
exit

这中这种方式自动关闭窗口,

(3)定义stop.bat文件关闭进程

@echo off
taskkill -f -t -im javaw.exe
exit

3.更多方式参考:Windows 下后台启动 jar 包,UTF-8 启动 jar 包_windows启动jar包-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值