上一篇已經介紹了Nacos的基礎知識(傳送門),也從源碼啓動了Nacos Server,今天我們探究下項目結構,以便於搭建自己的服務端!
我們先看下源碼中Nacos-console 項目的結構:
目錄包含了config(配置)、controller(控制層)、exception(自定義異常)、filter(過濾器)、model、security.nacos(安全模塊)、utils(工具類),定義習慣也是通用定義,可以從目錄結構、類名稱直接認識到其實現的功能。下面介紹幾個核心文件:
-
config目錄下的ConsoleConfig.java:
@Component
@EnableScheduling
@PropertySource("/application.properties")
public class ConsoleConfig {
@Autowired
private ControllerMethodsCache methodsCache;
@PostConstruct
public void init() {
methodsCache.initClassMethod("com.alibaba.nacos.naming.controllers");
methodsCache.initClassMethod("com.alibaba.nacos.console.controller");
methodsCache.initClassMethod("com.alibaba.nacos.config.server.controller");
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.setMaxAge(18000L);
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
關於註解的解釋,請移步:Spring 相關注解說明(關注公衆號,打開菜單)
這裏主要實現了兩個方法,init()和corsFilter()
init():主要利用反射的方法初始化了相關類,initClassMethod的實現:
public void initClassMethod(String packageName) {
Reflections reflections = new Reflections(packageName);
Set<Class<?>> classesList = reflections.getTypesAnnotatedWith(RequestMapping.class);
for (Class clazz : classesList) {
initClassMethod(clazz);
}
}
corsFilter():主要爲了解決跨域問題,它允許瀏覽器向跨域服務器發送Ajax請求,打破了Ajax只能訪問本站內的資源限制。分佈式微服務架構這幾乎是一定會遇到的問題。
2.controller目錄下的控制器:
HealthController:主要是健康檢查、節點檢查,具體在那個功能生效,後續再進行說明
NamespaceController:命名空間管理
PermissionController:權限認證
RoleController:角色管理
ServerStateController:節點信息
UserController:用戶管理,包含登陸等
這裏代碼不在詳細說明,
3.filter目錄下JwtAuthenticationTokenFilter:
主要功能校驗令牌是否有效
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
String jwt = resolveToken(request);
if (StringUtils.isNotBlank(jwt) && SecurityContextHolder.getContext().getAuthentication() == null) {
this.tokenManager.validateToken(jwt);
Authentication authentication = this.tokenManager.getAuthentication(jwt);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
/**
* Get token from header
*/
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader(NacosAuthConfig.AUTHORIZATION_HEADER);
if (StringUtils.isNotBlank(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)) {
return bearerToken.substring(7);
}
String jwt = request.getParameter(Constants.ACCESS_TOKEN);
if (StringUtils.isNotBlank(jwt)) {
return jwt;
}
return null;
}
4.security.nacos
主要包含一些具體的實現類,比如用戶登錄的邏輯(NacosAuthManager),令牌校驗和生成的邏輯(JwtTokenManager)
配置本地數據庫啓動
Nacos 本身在單機模式時nacos使用嵌入式數據庫實現數據的存儲,在0.7版本增加了支持mysql數據源能力,具體步驟是:
-
1.安裝數據庫,版本要求:5.6.5+(之前已安裝了一主二從數據庫)
-
2.初始化mysql數據庫,數據庫初始化文件:nacos-mysql.sql
-
3.修改conf/application.properties文件,增加支持mysql數據源配置(目前只支持mysql),添加mysql數據源的url、用戶名和密碼。
spring.datasource.platform=mysql
db.num=3
db.user=root
db.password=123456
db.url.0=jdbc:mysql://192.168.65.129:3000/nacos_config?serverTimezone=GMT%2B8&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.url.1=jdbc:mysql://192.168.65.129:3001/nacos_config?serverTimezone=GMT%2B8&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.url.2=jdbc:mysql://192.168.65.129:3002/nacos_config?serverTimezone=GMT%2B8&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
配置完畢,啓動Nacos.java 即可。
Connected to the target VM, address: '127.0.0.1:50380', transport: 'socket'
,--.
,--.'|
,--,: : | Nacos
,`--.'`| ' : ,---. Running in stand alone mode, All function modules
| : : | | ' ,'\ .--.--. Port: 8848
: | \ | : ,--.--. ,---. / / | / / ' Pid: 21524
| : ' '; | / \ / \. ; ,. :| : /`./ Console: http://192.168.19.1:8848/nacos/index.html
' ' ;. ;.--. .-. | / / '' | |: :| : ;_
| | | \ | \__\/: . .. ' / ' | .; : \ \ `. https://nacos.io
' : | ; .' ," .--.; |' ; :__| : | `----. \
| | '`--' / / ,. |' | '.'|\ \ / / /`--' /
' : | ; : .' \ : : `----' '--'. /
; |.' | , .-./\ \ / `--'---'
'---' `--`---' `----'
2020-04-21 23:54:23.186 INFO 21524 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration' of type [org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration$$EnhancerBySpringCGLIB$$f3ad21c2] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-04-21 23:54:23.310 INFO 21524 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'objectPostProcessor' of type [org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-04-21 23:54:23.314 INFO 21524 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler@30f28b5' of type [org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-04-21 23:54:23.315 INFO 21524 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration' of type [org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration$$EnhancerBySpringCGLIB$$1881c474] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-04-21 23:54:23.322 INFO 21524 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'methodSecurityMetadataSource' of type [org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-04-21 23:54:24.281 INFO 21524 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8848 (http)
2020-04-21 23:54:24.449 INFO 21524 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 3450 ms
2020-04-21 23:54:31.734 INFO 21524 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-04-21 23:54:31.930 INFO 21524 --- [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page: class path resource [static/index.html]
2020-04-21 23:54:32.513 INFO 21524 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: Ant [pattern='/**'], []
2020-04-21 23:54:32.546 INFO 21524 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@3b17759c, org.springframework.security.web.context.SecurityContextPersistenceFilter@6956eb58, org.springframework.security.web.header.HeaderWriterFilter@52fe87e0, org.springframework.security.web.csrf.CsrfFilter@458b4487, org.springframework.security.web.authentication.logout.LogoutFilter@153d14e3, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@6127ef86, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@301f9aa0, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@11cdf948, org.springframework.security.web.session.SessionManagementFilter@4d3a7f83, org.springframework.security.web.access.ExceptionTranslationFilter@73818435]
2020-04-21 23:54:32.638 INFO 21524 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator'
2020-04-21 23:54:32.670 INFO 21524 --- [ main] o.s.s.c.ThreadPoolTaskScheduler : Initializing ExecutorService 'taskScheduler'
2020-04-21 23:54:32.781 INFO 21524 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8848 (http) with context path '/nacos'
2020-04-21 23:54:32.790 INFO 21524 --- [ main] c.l.StartingSpringApplicationRunListener : Nacos logs files: C:\Users\back_\nacos\logs\
2020-04-21 23:54:32.790 INFO 21524 --- [ main] c.l.StartingSpringApplicationRunListener : Nacos conf files: C:\Users\back_\nacos\conf\
2020-04-21 23:54:32.790 INFO 21524 --- [ main] c.l.StartingSpringApplicationRunListener : Nacos data files: C:\Users\back_\nacos\data\
2020-04-21 23:54:32.790 INFO 21524 --- [ main] c.l.StartingSpringApplicationRunListener : Nacos started successfully in stand alone mode.
2020-04-21 23:54:32.913 INFO 21524 --- [nio-8848-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2020-04-21 23:54:32.923 INFO 21524 --- [nio-8848-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 10 ms
訪問:http://localhost:8848/nacos/,登錄賬戶/密碼:nacos/nacos
隨意添加配置,
這時,我們的數據庫config_info表中已經添加了這條記錄
從庫也同步更新
搭建自己的NacosServer
創建新的SpringBoot項目:
項目創建完成後
爲了方便測創建試,這裏先把功能性代碼直接複製到新項目,並解決引入問題
目的是先完成項目啓動,並在解決引入問題過程中,熟悉項目依賴和項目結構,以及功能。
把Nacos-console 項目中以下目錄完整複製到我們的項目中,
com/alibaba/nacos/console
console/src/main/resources/META-INF
console/src/main/resources/static
整理過的pom.xml 配置:
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>7.0.59</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-config</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-naming</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-core</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.10.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.10.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.10.5</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>release-nacos</id>
<build>
<finalName>nacos-server</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.1.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
在啓動類上添加掃描註解:
@ComponentScan("com.reims.main,com.alibaba.nacos")
@SpringBootApplication
public class NacosServerApplication {
public static void main(String[] args) {
SpringApplication.run(NacosServerApplication.class, args);
}
}
設置啓動單機模式:
在application.properties中添加mysql數據源:
spring.datasource.platform=mysql
db.num=3
db.user=root
db.password=123456
db.url.0=jdbc:mysql://192.168.65.129:3000/nacos_config?serverTimezone=GMT%2B8&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.url.1=jdbc:mysql://192.168.65.129:3001/nacos_config?serverTimezone=GMT%2B8&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.url.2=jdbc:mysql://192.168.65.129:3002/nacos_config?serverTimezone=GMT%2B8&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
切記,在resources目錄下的nacos-default.properties一定要複製過來,因爲這裏麪包含着Nacoe server 核心程序Nacos Config 的默認配置,比如:
useAddressServer=true
# whether open interInterFaceFilter; true:open; false:close; if open, others can't call inner interface. default:false
openInnerInterfaceFilter=false
# quickStart stip dumpAll;only dump change config
isQuickStart=false
# server notify each otherd
notifyConnectTimeout=200
# server notify each other
notifySocketTimeout=8000
# whether health check
isHealthCheck=true
# health check max fail count
maxHealthCheckFailCount=12
下節我們將全部自主搭建的時候再來講解這個文件的內容,以及怎麼去掉這個文件。
此時啓動NacosServerApplication,
Connected to the target VM, address: '127.0.0.1:52415', transport: 'socket'
,--.
,--.'|
,--,: : | Nacos
,`--.'`| ' : ,---. Running in stand alone mode, All function modules
| : : | | ' ,'\ .--.--. Port: 8848
: | \ | : ,--.--. ,---. / / | / / ' Pid: 18280
| : ' '; | / \ / \. ; ,. :| : /`./ Console: http://192.168.19.1:8848/nacos/index.html
' ' ;. ;.--. .-. | / / '' | |: :| : ;_
| | | \ | \__\/: . .. ' / ' | .; : \ \ `. https://nacos.io
' : | ; .' ," .--.; |' ; :__| : | `----. \
| | '`--' / / ,. |' | '.'|\ \ / / /`--' /
' : | ; : .' \ : : `----' '--'. /
; |.' | , .-./\ \ / `--'---'
'---' `--`---' `----'
2020-04-23 00:00:56.606 INFO 18280 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration' of type [org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-04-23 00:00:56.717 INFO 18280 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'objectPostProcessor' of type [org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-04-23 00:00:56.719 INFO 18280 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler@2c0c4c0a' of type [org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-04-23 00:00:56.720 INFO 18280 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration' of type [org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-04-23 00:00:56.724 INFO 18280 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'methodSecurityMetadataSource' of type [org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-04-23 00:00:57.606 INFO 18280 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8848 (http)
2020-04-23 00:00:57.717 INFO 18280 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 2908 ms
2020-04-23 00:01:04.717 INFO 18280 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-04-23 00:01:04.823 INFO 18280 --- [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page: class path resource [static/index.html]
2020-04-23 00:01:05.087 INFO 18280 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: Ant [pattern='/**'], []
2020-04-23 00:01:05.121 INFO 18280 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@189e96af, org.springframework.security.web.context.SecurityContextPersistenceFilter@e3692ca, org.springframework.security.web.header.HeaderWriterFilter@3791af, org.springframework.security.web.csrf.CsrfFilter@578d5d02, org.springframework.security.web.authentication.logout.LogoutFilter@78bce1c4, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@5cfe28e1, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@15ee8861, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@45b6c666, org.springframework.security.web.session.SessionManagementFilter@5535e9, org.springframework.security.web.access.ExceptionTranslationFilter@397dfbe8]
2020-04-23 00:01:05.140 INFO 18280 --- [ main] o.s.s.c.ThreadPoolTaskScheduler : Initializing ExecutorService 'taskScheduler'
2020-04-23 00:01:05.148 INFO 18280 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator'
2020-04-23 00:01:05.238 INFO 18280 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8848 (http) with context path '/nacos'
2020-04-23 00:01:05.244 INFO 18280 --- [ main] c.l.StartingSpringApplicationRunListener : Nacos logs files: C:\Users\back_\nacos\logs\
2020-04-23 00:01:05.244 INFO 18280 --- [ main] c.l.StartingSpringApplicationRunListener : Nacos conf files: C:\Users\back_\nacos\conf\
2020-04-23 00:01:05.244 INFO 18280 --- [ main] c.l.StartingSpringApplicationRunListener : Nacos data files: C:\Users\back_\nacos\data\
2020-04-23 00:01:05.245 INFO 18280 --- [ main] c.l.StartingSpringApplicationRunListener : Nacos started successfully in stand alone mode.
2020-04-23 00:01:05.590 INFO 18280 --- [nio-8848-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2020-04-23 00:01:05.598 INFO 18280 --- [nio-8848-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 8 ms
訪問:http://localhost:8848/nacos/,使用nacos/nacos 登錄,即可看到系統界面
結論:本章從簡單解析Nacos console 樣例工程,並仿照搭建了自己的Nacos Server,但是現在的邏輯都是基於原始樣例,不過本章的目的也不在於直接瞭解源碼寫法,而是從使用的角度,先入門,在使用的過程中去深入的瞭解每個模塊之間的結構和實現,瞭解更深的技術點。達到學得會,也能用自己的角度解釋出來。這也是我的觀念,先會用,有了場景再去深入,遠遠比乾巴巴的一對技術原理要實用的多,並不是每個人都是技術專家,都能成爲技術專家。只有在更多的使用中發現更多的問題,豐富的解決問題的經驗也將會是你的財富。
這一章,我從對Nacos源碼零瞭解寫出來,中間也遇到了不少問題,就那個切記提到的配置文件,就讓我疑惑了好久。不夠一個一個問題解決下來,現在我們對整個依賴的機構至少有了個初步的理解不是麼?也成功的從整個Nacos-all 體系代碼中,單獨分離出來了服務端。這樣我們以後不管是搭建自己的服務端,還是擴展前端的管理能力,我們對結構都有清晰的認識,怎麼做也就有了自己的想法。比如構建自己的前臺管理頁面融入到現有的管理系統,純後端做微服務集羣來管理,等等。
所以,包括以後,我的文章應該不會直接講一大堆的技術原理,除非有必要,我一般都會從認知的角度來出發,目的是從知道、瞭解到達會用,能說的出來,有自己的想法和邏輯。最後達到自己的一套常用的體系架構。並能隨意剪切調整,以便應對各種業務情況!
↓掃碼關注不迷路,更多操作持續更新中↓
↓掃碼關注,優惠先享↓