- 项目路径
- 必要环境
- 软件&版本&插件
- 项目结构
- 模块说明
- 服务启动顺序
- 具体实现
- 关键服务pom
- 父pom
- common pom
- gateway pom
- config pom
- eureka pom
- 实现EUREKA注册中心
- 启动类
- 配置文件(eureka服务不使用配置中心服务)
- 启动EUREKA服务
- 实现CONFIG配置中心
- 启动类
- 配置文件
- 启动CONFIG服务
- 实现GATEWAY网关服务
- 启动类
- GATEWAY配置类
- GATEWAY配置文件
- 启动GATEWAY服务
- 分别启动FALLBACK、MAINTAIN、WEBSOCKET服务
- 这三个服务没啥特别,配置文件和GATEWAY一样,读取对应的配置文件就好
- 调用GATEWAY对应路由规则的MAINTAIN服务提供的接口测试
- 最后启动ADMIN服务
- 启动类
- 访问 127.0.0.1:8887 测试
- OpenFeign示例
- WEBSOCKET服务接口
- MAINTAIN调用WEBSOCKET接口
- 总结
项目路径
GitHub路径:https://github.com/wuyue930912/cloud-pet-hub.git
Coding路径:https://e.coding.net/pethub/hub/pet-hub.git
必要环境
软件&版本&插件
| 数据库 | JDK | SpringBoot | SpringCloud | 插件 |
|---|---|---|---|---|
| mariadb | 8 | 2.2.1.RELEAS | Hoxton.SR1 | lombok |
项目结构
模块说明
| base | common | cloud-admin | cloud-config | cloud-eureka | cloud-fallback | cloud-gateway | cloud-maintain | cloud-websocket |
|---|---|---|---|---|---|---|---|---|
| 数据库SQL & 各服务配置文件路径(配置中心读取配置文件路径)& maven所用setting.xml文件 | 公共依赖 | Spring Boot Admin 服务 | 配置中心服务 | 注册中心服务 | 服务熔断FallBack服务 | 网关服务 | 业务服务, 提供简单接口测试 | 业务服务,提供WEBSOCKET服务 |
服务启动顺序
具体实现
关键服务pom
父pom
4.0.0 com.pet PETHUB 1.0-SNAPSHOT pom pethub-hub-master master https://pethub-maven.pkg.coding.net/repository/hub/master/ cloud-fallback cloud-gateway cloud-maintain cloud-eureka cloud-websocket cloud-admin cloud-config common 8 org.springframework.boot spring-boot-starter-parent 2.2.1.RELEASE org.springframework.cloud spring-cloud-dependencies Hoxton.SR1 pom import org.springframework.boot spring-boot-starter-actuator org.projectlombok lombok 1.18.20 org.springframework.boot spring-boot-starter-test test org.jsoup jsoup 1.14.3 commons-lang commons-lang 2.6 org.slf4j slf4j-log4j12 1.7.32 org.jboss.logging jboss-logging 3.3.0.Final com.fasterxml classmate 1.3.3 org.mapstruct mapstruct 1.4.2.Final org.mapstruct mapstruct-processor 1.4.2.Final cn.hutool hutool-crypto 5.7.15 cn.hutool hutool-core 5.7.15 org.apache.shiro shiro-spring 1.7.1 com.alibaba fastjson 1.2.78 io.springfox springfox-boot-starter 3.0.0 com.google.guava guava 15.0 org.springframework.boot spring-boot-starter-validation dev dev true release release
common pom
PETHUB com.pet 1.0-SNAPSHOT 4.0.0 common 8 8 org.springframework.boot spring-boot-starter-web org.hibernate hibernate-validator 5.4.1.Final javax.validation validation-api 1.1.0.Final org.mariadb.jdbc mariadb-java-client 2.7.3 com.alibaba druid 1.2.8 org.springframework.boot spring-boot-starter-data-jdbc org.springframework.boot spring-boot-starter-data-jpa org.springframework.cloud spring-cloud-starter-netflix-eureka-client com.sun.jersey jersey-client com.sun.jersey jersey-core com.sun.jersey.contribs jersey-apache-client4 org.springframework.cloud spring-cloud-starter-openfeign
gateway pom
PETHUB com.pet 1.0-SNAPSHOT 4.0.0 cloud-gateway 8 8 org.springframework.cloud spring-cloud-starter-openfeign org.springframework.cloud spring-cloud-starter-gateway org.springframework.cloud spring-cloud-starter-netflix-ribbon org.springframework.cloud spring-cloud-starter-netflix-hystrix org.springframework.cloud spring-cloud-config-client org.springframework.cloud spring-cloud-starter-netflix-eureka-client com.sun.jersey jersey-client com.sun.jersey jersey-core com.sun.jersey.contribs jersey-apache-client4 de.codecentric spring-boot-admin-starter-client 2.2.4 src/main/resources true application.yml application-dev.yml application-prod.yml src/main/resources true application.yml application-${profileActive}.yml org.springframework.boot spring-boot-maven-plugin
config pom
PETHUB com.pet 1.0-SNAPSHOT 4.0.0 cloud-config 8 8 com.pet common 1.0-SNAPSHOT org.springframework.cloud spring-cloud-config-server de.codecentric spring-boot-admin-starter-client 2.2.4 src/main/resources true application.yml application-dev.yml application-prod.yml src/main/resources true application.yml application-${profileActive}.yml org.springframework.boot spring-boot-maven-plugin
eureka pom
PETHUB com.pet 1.0-SNAPSHOT 4.0.0 cloud-eureka 8 8 org.springframework.cloud spring-cloud-starter-netflix-eureka-server de.codecentric spring-boot-admin-starter-client 2.2.4 org.springframework.boot spring-boot-starter-security src/main/resources true application.yml application-dev.yml application-prod.yml src/main/resources true application.yml application-${profileActive}.yml org.springframework.boot spring-boot-maven-plugin
实现EUREKA注册中心
启动类
package com.pet;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@SpringBootApplication
@EnableEurekaServer
@Slf4j
public class EurekaStarter {
public static void main(String[] args) {
SpringApplication.run(EurekaStarter.class, args);
log.info("start running eureka server : 【{}】", "http://127.0.0.1:8888");
}
@EnableWebSecurity
static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().ignoringAntMatchers("/eureka/**");
super.configure(http);
}
}
}
配置文件(eureka服务不使用配置中心服务)
server:
port: 8888
logging:
file:
path: D:\LOG
eureka:
instance:
hostname: 127.0.0.1
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
server:
eviction-interval-timer-in-ms: 6000
wait-time-in-ms-when-sync-empty: 6000
enable-self-preservation: true
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS
spring:
application:
name: eureka
security:
user:
name: eureka
password: 设置EUREKA登录密码
roles: SUPERUSER
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
启动EUREKA服务
启动成功访问 ip:8888端口出现下面页面说明配置成功。
启动类
package com.pet;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
@Slf4j
public class ConfigStarter {
public static void main(String[] args) {
SpringApplication.run(ConfigStarter.class, args);
log.info("start running config server");
}
}
配置文件
server:
port: 8887
max-http-header-size: 20480
servlet:
session:
timeout: PT30M
logging:
file:
path: D:\LOG
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://EUREKA账号:EUREKA密码@127.0.0.1:8888/eureka
instance:
prefer-ip-address: true
ip-address: 127.0.0.1
instance-id: 127.0.0.1:${spring.application.name}:${server.port}
feign:
hystrix:
enabled: true
spring:
cloud:
config:
server:
git:
uri: https://e.coding.net/pethub/hub/pet-hub.git ## git路径
username: git用户名
password: git密码
search-paths: base/script/config/application-* ## 配置文件存放具体路径
default-label: master ## 指定分支
application:
name: config
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
datasource:
url: jdbc:mariadb://127.0.0.1:3306/pet_hub?characterEncoding=utf-8&useSSL=false&useTimezone=true&serverTimezone=GMT%2B8
driver-class-name: org.mariadb.jdbc.Driver
username: root
password: 数据库密码
type: com.alibaba.druid.pool.DruidDataSource
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECt 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
jpa:
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
database: MYSQL
show-sql: true
open-in-view: false
servlet:
multipart:
enabled: true
max-file-size: 10MB
max-request-size: 10MB
# REDIS 配置
redis:
database: 0
host: 127.0.0.1
port: 6379
password:
timeout: 6000
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 10
min-idle: 2
启动CONFIG服务
启动成功后,通过postman调用 127.0.0.1:8887/{服务名}/{版本号}接口,查看是否成功(第一次调用可能会比较慢,泡壶茶水儿,等一会儿~ )。
出现下面响应,说明配置成功。
启动类
package com.pet;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
@Slf4j
public class GatewayStarter {
public static void main(String[] args) {
SpringApplication.run(GatewayStarter.class, args);
log.info("start running gateway server : 【{}】", "http://127.0.0.1");
}
}
GATEWAY配置类
package com.pet.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
@Configuration
public class GatewayConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
// 支持中文编码
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
return restTemplate;
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
httpRequestFactory.setReadTimeout(5000);// 单位为ms
httpRequestFactory.setConnectTimeout(5000);// 单位为ms
return httpRequestFactory;
}
}
GATEWAY配置文件
后面的服务通过CONFIG配置中心读取配置文件!此时配置文件名为bootstrap.yml!
application.yml中啥也没有!
bootstrap.yml
server:
port: 80
spring:
application:
name: gateway
cloud:
config:
uri: http://localhost:8887
label: master
name: gateway
profile: dev
discovery:
enabled: true
service-id: config
fail-fast: true
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://EUREKA用户名:EUREKA密码@127.0.0.1:8888/eureka
instance:
prefer-ip-address: true
ip-address: 127.0.0.1
instance-id: 127.0.0.1:${spring.application.name}:${server.port}
真正读取的配置文件:
server:
port: 80
logging:
file:
path: D:\LOG
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://EUREKA账号:EUREKA密码@127.0.0.1:8888/eureka
instance:
prefer-ip-address: true
ip-address: 127.0.0.1
instance-id: 127.0.0.1:${spring.application.name}:${server.port}
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS
spring:
application:
name: gateway
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
cloud:
gateway:
routes:
# maintain服务路由
- id: maintain
uri: lb://maintain
predicates:
- Path=/maintain/**
filters:
- StripPrefix=1
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback/error
# websocket服务路由
- id: websocket
uri: lb://websocket
predicates:
- Path=/websocket/**
filters:
- StripPrefix=1
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback/error1
# fallback服务路由
- id: fallback
uri: lb://fallback
predicates:
- Path=/fallback/**
# eureka服务路由
- id: eureka
uri: http://127.0.0.1:8888
predicates:
- Path=/eureka/**
filters:
- StripPrefix=1
# admin服务路由
- id: admin
uri: lb://admin
predicates:
- Path=/admin/**
filters:
- StripPrefix=1
feign:
hystrix:
enabled: true
hystrix:
command:
default:
execution:
isolation:
thread:
# 全局熔断器5s超时
timeoutInMilliseconds: 5000
启动GATEWAY服务
这里gateway用了80端口,因为里面配置了一个指向EUREKA的路由,所以此时可以通过访问 127.0.0.1/eureka 来测试是否成功(测试后可以删除EUREKA路由,没啥用)
出现下面页面,说明配置成功。
分别启动FALLBACK、MAINTAIN、WEBSOCKET服务
这三个服务没啥特别,配置文件和GATEWAY一样,读取对应的配置文件就好
也可以启动多个实例,启动多实例方法(先修改好git上的配置文件的端口!然后再配置IDEA!):
调用GATEWAY对应路由规则的MAINTAIN服务提供的接口测试
路由规则:
FALLBACK服务提供的接口:
package com.pet.controller;
import com.pet.config.ServiceException;
import com.pet.constant.ErrorMsgConstant;
import com.pet.vo.ResponseResultVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/fallback")
public class FallBackController {
@RequestMapping("/error")
public ResponseEntity> fallback() {
log.error(ErrorMsgConstant.MAINTAIN_ERROR);
throw new ServiceException(ErrorMsgConstant.MAINTAIN_ERROR);
}
@RequestMapping("/error1")
public ResponseEntity> fallback1() {
log.error(ErrorMsgConstant.WEBSOCKET_ERROR);
throw new ServiceException(ErrorMsgConstant.WEBSOCKET_ERROR);
}
}
测试成功情况:
kill掉MATINTAIN服务,测试服务熔断:
forward到fallback服务的接口,说明配置成功:
最后启动ADMIN服务
启动类
package com.pet;
import de.codecentric.boot.admin.server.config.EnableAdminServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableDiscoveryClient
@EnableAdminServer
@Slf4j
public class AdminStarter {
public static void main(String[] args) {
SpringApplication.run(AdminStarter.class, args);
log.info("start running admin server");
}
}
访问 127.0.0.1:8887 测试
出现下面页面,说明配置成功,具体作用大家自己探索把~
OpenFeign示例
WEBSOCKET服务接口
调用此方法可以推送websocket消息到页面。
package com.pet.controller;
import com.pet.constant.RoomsConstant;
import com.pet.event.entity.LogToDbEventEntity;
import com.pet.servers.SystemInfoServer;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class PushLogController {
private final SystemInfoServer systemInfoServer;
@PostMapping("/pushLog")
public void pushLogToWeb(@RequestBody LogToDbEventEntity log) {
systemInfoServer.sendMsg(RoomsConstant.SYSTEM_INFO, log.toString());
}
}
MAINTAIN调用WEBSOCKET接口
package com.pet.service.manager;
import com.pet.event.entity.LogToDbEventEntity;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@FeignClient(value = "websocket")
public interface PushLogService {
@PostMapping(value = "/pushLog", consumes = "application/json")
void pushDateToWeb(@RequestBody LogToDbEventEntity log);
}
MAINTAIN服务切面代码,在所用使用了@LogController注解的方法,调用WEBSOCKET服务,推送消息。
package com.pet.config.aop;
import com.pet.annotation.LogController;
import com.pet.config.ServiceException;
import com.pet.constant.ErrorMsgConstant;
import com.pet.constant.HttpConstant;
import com.pet.event.entity.LogToDbEventEntity;
import com.pet.po.SysUser;
import com.pet.service.manager.PushLogService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Date;
import java.util.Objects;
@RequiredArgsConstructor
@Aspect
@Component
@Slf4j
public class PushLogAspect {
private final PushLogService pushLogService;
@Pointcut("@annotation(com.pet.annotation.LogController)")
public void pushPoint() {
}
@Before(value = "pushPoint() && @annotation(logController)", argNames = "joinPoint, logController")
public void beforeController(JoinPoint joinPoint, LogController logController) {
HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
HttpSession session = request.getSession();
SysUser user = (SysUser) session.getAttribute(HttpConstant.SESSION_USER);
String realMethodName = joinPoint.getSignature().getName();
String requestIp = getIp(request);
try {
pushLogService.pushDateToWeb(LogToDbEventEntity.builder()
.date(new Date())
.userName(Objects.isNull(user) ? "system" : user.getUserName())
.method(logController.method())
.logLevel(logController.logLevel())
.description(logController.description())
.realMethod(realMethodName)
.ip(requestIp)
.build());
} catch (Exception e) {
throw new ServiceException(ErrorMsgConstant.WEBSOCKET_ERROR);
}
}
public static String getIp(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-AuthenticationIp");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-AuthenticationIp");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
总结
大佬们如果clone下来可以用的话,给个免费的star吧~



