閑來無事,總結一下公司使用的 微服務框架,文章所有代碼GtiHub:https://github.com/Tom-shushu/work-study 裡面的gateway-server和server1項目 1、Docker 部署 Nacos 資料庫準備 新建 "nacos_config" 資料庫 # ...
閑來無事,總結一下公司使用的 微服務框架,文章所有代碼GtiHub:https://github.com/Tom-shushu/work-study 裡面的gateway-server和server1項目
1、Docker 部署 Nacos
資料庫準備
新建 "nacos_config" 資料庫
在https://github.com/alibaba/nacos/blob/develop/distribution/conf/nacos-mysql.sql 獲取最新的SQL腳本並執行(註意版本一定要對應),執行成功後如下圖所示:
2、安裝Nacos
a.下載鏡像
docker pull nacos/nacos-server
b.新建 application.properties文件(註意鑒權那個一定要加-不然會出現許可權繞過漏洞 CVE-2021-29441)前幾天的護網行動可把人忙壞了!
### 開啟鑒權
nacos.core.auth.enabled=true
### 關閉使用user-agent判斷服務端請求並放行鑒權的功能
nacos.core.auth.enable.userAgentAuthWhite=false
### 配置自定義身份識別的key(不可為空)和value(不可為空)
nacos.core.auth.server.identity.key=my-nacos
nacos.core.auth.server.identity.value=my-nacos
server.contextPath=/nacos
server.servlet.contextPath=/nacos
server.port=8848
spring.datasource.platform=mysql
db.num=1
## 修改為自己的資料庫連接和密碼
db.url.0=jdbc:mysql://xxxxxxxxxxx.mysql.rds.aliyuncs.com:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=xxxxxxx
db.password=xxxxxxx
nacos.cmdb.dumpTaskInterval=3600
nacos.cmdb.eventTaskInterval=10
nacos.cmdb.labelTaskInterval=300
nacos.cmdb.loadDataAtStart=false
management.metrics.export.elastic.enabled=false
management.metrics.export.influx.enabled=false
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i
nacos.security.ignore.urls=/,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/login,/v1/console/health/**,/v1/cs/**,/v1/ns/**,/v1/cmdb/**,/actuator/**,/v1/console/server/**
nacos.naming.distro.taskDispatchThreadCount=1
nacos.naming.distro.taskDispatchPeriod=200
nacos.naming.distro.batchSyncKeyCount=1000
nacos.naming.distro.initDataRatio=0.9
nacos.naming.distro.syncRetryDelay=5000
nacos.naming.data.warmup=true
nacos.naming.expireInstance=true
c.直接啟動
docker run --name nacos-server -p 8848:8848 --privileged=true --restart=always -e MODE=standalone -e PREFER_HOST_MODE=hostname -v /root/nacos/logs:/home/nacos/logs -v /root/application.properties:/home/nacos/conf/application.properties -d nacos/nacos-server
d.測試,註意打開8848安全組
瀏覽器輸入: IP:8848/nacos 預設賬號密碼: nacos nacos
3、Nginx 代理 介面功能變數名稱(沒有功能變數名稱的可以跳過)
功能變數名稱有ssl證書的修改nginx.conf文件,網關我們使用8000埠,現在一次配好
# HTTPS server
server {
listen 443 ssl;
server_name api.zhouhong.icu;
charset utf-8;
ssl_certificate /usr/local/nginx/ssl/8247790_api.zhouhong.icu.pem;
ssl_certificate_key /usr/local/nginx/ssl/8247790_api.zhouhong.icu.key;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
## Nacos
location /nacos {
proxy_pass http://127.0.0.1:8848;
}
## 網關
location / {
proxy_pass http://127.0.0.1:8000;
}
}
沒有ssl安全證書的可以如下配置,網關我們使用8000埠,現在一次配好
server {
listen 80;
server_name api.zhouhong.icu;
## Nacos
location /nacos {
proxy_pass http://127.0.0.1:8848;
}
## 網關
location / {
proxy_pass http://127.0.0.1:8000;
}
}
測試
瀏覽器輸入: https://api.zhouhong.icu/nacos 或者 http://api.zhouhong.icu/nacos
4、網關服務搭建(代碼參考:https://github.com/Tom-shushu/work-study)
1.pom文件(只展示部分,詳細信息請到github看代碼~)
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<exclusions>
<exclusion>
<artifactId>fastjson</artifactId>
<groupId>com.alibaba</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<exclusions>
<exclusion>
<artifactId>fastjson</artifactId>
<groupId>com.alibaba</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
2.配置文件 application.yml
#請求和響應GZIP壓縮支持
feign:
httpclient:
enabled: false
okhttp:
enabled: true
compression:
request:
enabled: true
mime-types: text/xml,application/xml,application/json
min-request-size: 2048
response:
enabled: true
spring:
cloud:
gateway:
enabled: true
discovery:
locator:
lowerCaseServiceId: true
enabled: true
## 配置下麵這個預設會匹配以服務名為首碼的路由,就不需要在nacos裡面配置路由了
filters:
- name: RewritePath
args:
regexp: "'/' + serviceId + '/(?<remaining>.*)'"
replacement: "'/' + serviceId + '/${remaining}'"
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Origin
globalcors:
add-to-simple-url-handler-mapping: true
corsConfigurations:
'[/**]':
allowed-origins: "*"
allowed-methods: "*"
allowed-headers: "*"
allow-credentials: true
httpclient:
response-timeout: 60000
connect-timeout: 60000
loadbalancer:
ribbon:
enabled: false
application:
name: gateway
sleuth:
enabled: false
http:
legacy:
enabled: true
main:
allow-bean-definition-overriding: true
codec:
max-in-memory-size: 20MB
management:
endpoints:
web:
exposure:
include: '*'
exclude: heapdump,dump,threaddump,configprops,env
security:
enabled: false
gate:
ignore:
startWith: /auth/jwt,/auth/captcha
ribbon:
eureka:
enabled: true
ReadTimeout: 60000
ConnectTimeout: 60000
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 1
OkToRetryOnAllOperations: false
hystrix:
threadpool:
default:
coreSize: 1000 ##併發執行的最大線程數,預設10
maxQueueSize: 1000 ##BlockingQueue的最大隊列數
queueSizeRejectionThreshold: 500 ##即使maxQueueSize沒有達到,達到queueSizeRejectionThreshold該值後,請求也會被拒絕
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 10000
timerDelayInMilliseconds: 10000
3.bootstrap.yml 配置文件
#服務配置
server:
port: 8000
max-http-header-size: 10240
spring:
application:
name: gateway
profiles:
active: @spring.active@
servlet:
multipart:
max-request-size: 100MB
max-file-size: 100MB
logging:
file:
path: logs/
---
spring:
profiles: local
cloud:
nacos:
config:
server-addr: https://api.zhouhong.icu
namespace: 4d7c95f1-21d5-43ad-a420-5eebb2473751
file-extension: yml
username: nacos
password: nacos
discovery:
server-addr: https://api.zhouhong.icu
namespace: 4d7c95f1-21d5-43ad-a420-5eebb2473751
username: nacos
password: nacos
---
spring:
profiles: dev
cloud:
nacos:
config:
server-addr: https://api.zhouhong.icu
namespace: 07610e11-85ae-49cb-aa46-b8f0802543bd
file-extension: yml
username: nacos
password: nacos
discovery:
server-addr: https://api.zhouhong.icu
namespace: 07610e11-85ae-49cb-aa46-b8f0802543bd
username: nacos
password: nacos
---
spring:
profiles: prod
cloud:
nacos:
config:
server-addr: https://api.zhouhong.icu
namespace: 01940467-9f25-45e3-b9b8-1ad25c937544
file-extension: yml
extension-configs:
discovery:
server-addr: https://api.zhouhong.icu
namespace: 01940467-9f25-45e3-b9b8-1ad25c937544
4.網關配置類GatewayConfig
@Configuration
public class GatewayConfig {
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public Decoder feignDecoder() {
return new ResponseEntityDecoder(new SpringDecoder(feignHttpMessageConverter()));
}
public ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(new GateWayMappingJackson2HttpMessageConverter());
return new ObjectFactory<HttpMessageConverters>() {
@Override
public HttpMessageConverters getObject() throws BeansException {
return httpMessageConverters;
}
};
}
public class GateWayMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
GateWayMappingJackson2HttpMessageConverter() {
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.valueOf(MediaType.TEXT_HTML_VALUE + ";charset=UTF-8"));
setSupportedMediaTypes(mediaTypes);
}
}
@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder().exchangeStrategies(ExchangeStrategies.builder().codecs(
clientCodecConfigurer -> clientCodecConfigurer.defaultCodecs().maxInMemorySize(10 * 1024 * 1024)).build());
}
}
5.過濾器
package com.zhouhong.gatewayserver.core.filter;
/**
* 全局請求轉換
**/
@Log4j2
@Component
public class RequestGlobalFilter implements GlobalFilter, Ordered {
@Resource
ServerCodecConfigurer codecConfigurer;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (Objects.equals(exchange.getRequest().getMethod(), HttpMethod.POST)) {
// 表單傳輸
MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(mediaType)) {
return returnMono(chain, exchange);
}
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
//放開不進行安全過濾的介面
boolean checkSign = true;
for (String notAuthResource : ReleaseConstant.NONE_SECURITY_URL_PATTERNS) {
AntPathMatcher antPathMatcher = new AntPathMatcher();
if (antPathMatcher.match(notAuthResource, path)) {
checkSign = false;
}
}
ServerRequest serverRequest = ServerRequest.create(exchange, codecConfigurer.getReaders());
Mono modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
//因為約定了終端傳參的格式,所以只考慮json的情況,如果是表單傳參,請自行發揮
if (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)) {
JSONObject jsonObject = JSON.parseObject(body);
String newBody = null;
try {
// 如果是1.0版本的請求(body經過base64轉碼),要將body解碼
if (jsonObject.containsKey("sign") || jsonObject.containsKey("object")) {
newBody = verifySignature(jsonObject.getString("object"));
} else {
newBody = body;
}
} catch (Exception e) {
return Mono.error(e);
}
log.info("請求:" + path + " " + body);
return Mono.just(newBody);
}
return Mono.just(body);
});
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
HttpHeaders headers = new HttpHeaders();
headers.putAll(exchange.getRequest().getHeaders());
headers.remove("Content-Length");
// 請求中添加TOKEN用戶信息
addTokenInfo(headers, serverRequest);
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
Mono mono = bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
ServerHttpRequest decorator = this.decorate(exchange, headers, outputMessage);
return returnMono(chain, exchange.mutate().request(decorator).build());
}));
return mono;
} else {
//GET 驗簽
return returnMono(chain, exchange);
}
}
@Override
public int getOrder() {
return 1;
}
private Mono<Void> returnMono(GatewayFilterChain chain, ServerWebExchange exchange) {
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
}));
}
private String verifySignature(String paramStr) {
return new String(Base64Utils.decodeFromString(paramStr));
}
private ServerHttpRequestDecorator decorate(ServerWebExchange exchange, HttpHeaders headers, CachedBodyOutputMessage outputMessage) {
return new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public HttpHeaders getHeaders() {
long contentLength = headers.getContentLength();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(headers);
if (contentLength > 0L) {
httpHeaders.setContentLength(contentLength);
} else {
httpHeaders.set("Transfer-Encoding", "chunked");
}
return httpHeaders;
}
@Override
public Flux<DataBuffer> getBody() {
return outputMessage.getBody();
}
};
}
private void addTokenInfo(HttpHeaders headers, ServerRequest request) {
return;
}
}
其他代碼見github源碼~
4、普通業務服務(服務名稱:server1)
1.bootstrap.yml 配置文件
server:
port: 8080
max-http-header-size: 10240
spring:
jackson:
time-zone: GMT+8
main:
allow-bean-definition-overriding: true
profiles:
active: @spring.active@
application:
name: server1
logging:
file:
path: ./logs/
log:
path: ./logs/
---
spring:
profiles: local
cloud:
nacos:
config:
server-addr: https://api.zhouhong.icu
namespace: 4d7c95f1-21d5-43ad-a420-5eebb2473751
file-extension: yml
username: nacos
password: nacos
discovery:
server-addr: https://api.zhouhong.icu
namespace: 4d7c95f1-21d5-43ad-a420-5eebb2473751
username: nacos
password: nacos
---
spring:
profiles: dev
cloud:
nacos:
config:
server-addr: https://api.zhouhong.icu
namespace: 07610e11-85ae-49cb-aa46-b8f0802543bd
file-extension: yml
username: nacos
password: nacos
discovery:
server-addr: https://api.zhouhong.icu
namespace: 07610e11-85ae-49cb-aa46-b8f0802543bd
username: nacos
password: nacos
---
spring:
profiles: prod
cloud:
nacos:
config:
server-addr: https://api.zhouhong.icu
namespace: 01940467-9f25-45e3-b9b8-1ad25c937544
file-extension: yml
extension-configs:
discovery:
server-addr: https://api.zhouhong.icu
namespace: 01940467-9f25-45e3-b9b8-1ad25c937544
這裡只是為了測試簡單寫了兩個controller方法
/**
* 服務名稱和介面第一段字元匹配
* @return
*/
@PostMapping("/server1/test")
public String theSameServer() {
return "你好呀( ⊙ o ⊙ )!";
}
/**
* 服務名稱和介面第一段字元不匹配
* @return
*/
@PostMapping("/serverNotSame/test")
public String gettoken() {
return "你好呀( ⊙ o ⊙ )!";
}
5、綜合測試
A.部署說明:
1.項目中我按照正常的公司開發形式,指定了 本地(local)、測試/開發(dev)、生產(prod)環境,搭建闊以按照自己的需求進行取捨;
2.本次測試我用測試環境進行演示、大家需要在nacos上建立與環境對應的命名空間(local、dev、prod),並且需要在網關和自己業務服務裡面指定對應的環境和nacos配置;
3.之前說過nacos開啟了用戶鑒權,所以需要在discovery 和 config 下麵配置登錄時的用戶名以及密碼
4.本項目實現的是根據服務名稱自動匹配介面首碼,至於根據nacos路由列表匹配大家可以自行百度。
B.部署
1.打包:需要選擇對應的環境,我這裡使用測試環境(dev)如圖所示:
2.需要打包網關和業務服務兩個服務,最終會得到 gateway.jar 和 server1.jar兩個jar包,然後上傳伺服器
3.部署(我這裡為了方便就不需要進行docker部署了)直接使用 java -jar XXX.jar 進行簡單部署了
部署成功後在nocos控制台查看服務是否註冊成功
如果服務列表為這個熊樣,就說明部署成功,接下來就可以測試啦
4.介面測試
使用PostMan進行介面調用
A 調用和服務名稱相匹配的那個介面 https://api.zhouhong.icu/server1/test
如圖所示調用成功(因為yml裡面那個配置,它會自動根據服務名稱 server1 進行匹配)
B 調用與服務名稱不匹配的那個介面
攔截成功
本文來自博客園,作者:Tom-shushu,轉載請註明原文鏈接:https://www.cnblogs.com/Tom-shushu/p/16557185.html