java作品集:企業信息門戶webtap

作品背景

隨着企業應用的軟件越來越多,並且信息軟件基本以B/S為主了,很多時候各種軟件的地址,讓大家記的頭昏腦脹,並且一堆密碼要記,而且大部分系統之間無法互通,雖然市面上有各種集成方案,但無法做到簡單有效,都是大型軟件廠商的PPT解決方案加一堆開發工作和大量的成本支出,最重要的是大部分都是體驗極差、毫無美感的東西。

解決方案

基於上述背景,個人利用業餘時間在持續完善做一款小作品,或多或少的解決一點問題,雖然目前還沒成熟,但是考慮再三,先開源出來,希望有志同道合的人一起完善。

作品說明

1.首頁

首頁主要功能有

  • app显示
  • 文件夾分類
  • 應用搜索
  • 登錄
  • 登錄后快捷新增應用
  • 背景自動每天同步bing搜索引擎的的壁紙
  • 應用和新聞站點鏈接(未完成)

通過點擊應用上的鎖 icon即可查看應用的賬號和密碼,在沒有單點登錄的功能情況下這個功能非常有用

2.系統登錄

點擊首頁右上角的 sigin 到登錄頁面

3.應用列表

後台管理 主功能只有新建應用、應用列表、系統設置,極其簡約,好不好看只是個人風格,默認登錄進來及显示應用列表。

4、新增應用

添加應用除了常規功能還增加了敏感信息輸入,敏感信息只能登錄后才能查看;
查看密碼功能考慮到很多時候連接地址需要密碼才能訪問;
查看權限目前只實現了登錄可見以及自己可見(權限功能還需要繼續完善);

5、系統設置

系統設置里可以進行基本信息維護,個人登錄信息維護,用戶管理,app分類管理,及多組織管理,主要介紹以下2重點功能。

常規設置

基本設置里為當前組織的組織名稱,訪問短鏈接地址(多組織情況下),以及組織的logo

多組織管理

技術架構

技術棧

  • springboot
  • mysql5.7
  • gradle
  • thymeleaf
  • vue2.0

代碼結構

數據庫結構

源碼地址

https://github.com/robotbird/webtap

https://gitee.com/robotbird/webtap

使用方法

  • 1、mysql 新建webtap數據庫
  • 2、導入工程目錄下doc/db/webtap.sql
  • 3、設置好application-dev.properties 里的數據庫密碼,默認root/root
  • 4、打個war包放到tomcat下即可運行,這地方沒有用jar包的原因是考慮上傳目錄採用jar包不知道什麼樣的方式合適。
  • 5、登錄管理員默認賬號robotbird@qq.com,密碼123456(暫時只支持郵箱登錄)

在線體驗

體驗地址:http://webtap.cn/
由於服務器在國外,訪問時候還請耐心等候。

總結

作品當前還未實現的功能,企業內部信息搜索集成、單點登錄集成、權限管理,以及後續考慮的小程序功能,但是依然放出來,激勵自己繼續完善下去。
此作品完全個人原創,開源遵從GNU General Public License v3.0,版權所屬個人所有,如果有同學對這個作品比較感興趣可以微信聯繫robotbird798

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

OAuth + Security – 5 – Token存儲升級(數據庫、Redis)

PS:此文章為系列文章,建議從第一篇開始閱讀。

在我們之前的文章中,我們當時獲取到Token令牌時,此時的令牌時存儲在內存中的,這樣顯然不利於我們程序的擴展,所以為了解決這個問題,官方給我們還提供了其它的方式來存儲令牌,存儲到數據庫或者Redis中,下面我們就來看一看怎麼實現。

不使用Jwt令牌的實現

  • 存儲到數據庫中(JdbcTokenStore)

使用數據庫存儲方式之前,我們需要先準備好對應的表。Spring Security OAuth倉庫可以找到相應的腳本:https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql。該腳本為HSQL,所以需要根據具體使用的數據庫進行相應的修改,以MySQL為例,並且當前項目中,只需要使用到oauth_access_token和oauth_refresh_token數據表,所以將創建這兩個庫表的語句改為MySQL語句:

CREATE TABLE oauth_access_token (
	token_id VARCHAR ( 256 ),
	token BLOB,
	authentication_id VARCHAR ( 256 ),
	user_name VARCHAR ( 256 ),
	client_id VARCHAR ( 256 ),
	authentication BLOB,
	refresh_token VARCHAR ( 256 ) 
);

CREATE TABLE oauth_refresh_token ( 
token_id VARCHAR ( 256 ), 
token BLOB, authentication BLOB 
);

然後我們需要去配置對應的認證服務器,主要就是修改之前文章中TokenConfigure類中的tokenStore()方法:

// 同時需要注入數據源
@Autowired
private DataSource dataSource;
    
@Bean
    public TokenStore tokenStore() {
        return new JdbcTokenStore(dataSource);
    }

同時需要添加jdbc的依賴:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

認證服務器中的配置保持本系列第一章的配置不變,此處不再贅述。

其中若有不理解的地方,請參考該系列的第一篇文章

關於數據源的補充:

在我們項目中配置的數據源,可能不一定是使用的官方提供的格式,比如我們自定義的格式,或者使用第三方的數據源,那麼我們如何去配置呢?這裏以mybatis-plus的多數據源為例:

@Configuration
@EnableAuthorizationServer
public class FebsAuthorizationServerConfigure extends AuthorizationServerConfigurerAdapter {

    ......
    
    @Autowired
    private DynamicRoutingDataSource dynamicRoutingDataSource;

    @Bean
    public TokenStore tokenStore() {
        DataSource dimplesCloudBase = dynamicRoutingDataSource.getDataSource("dimples_cloud_base");
        return new JdbcTokenStore(febsCloudBase);
    }
    ......
}

然後啟動項目,重新獲取Token進行測試,能正確的獲取到Token:

我們查看數據中,看是否已經存入相關信息到數據庫中:

  • 存儲到redis(RedisTokenStore)

令牌存儲到redis中相比於存儲到數據庫中來說,存儲redis中,首先在性能上有一定的提升,其次,令牌都有有效時間,超過這個時間,令牌將不可再用,而redis的可以給對應的key設置過期時間,完美切合需求,所有令牌存儲到redis中也是一種值得使用的方法。

首先,我們需要在項目中添加redis依賴,同時配置redis

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

新建配置類RedisConfigure

@Configuration
public class RedisConfigure{

    @Bean
    @ConditionalOnClass(RedisOperations.class)
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(mapper);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key採用 String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的 key也採用 String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式採用 jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的 value序列化方式採用 jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();

        return template;
    }
    
}

配置redis的連接

spring:
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    lettuce:
      pool:
        min-idle: 8
        max-idle: 500
        max-active: 2000
        max-wait: 10000
    timeout: 5000

這時我們啟動項目測試,會發現項目將會報錯:

Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool2.impl.GenericObjectPoolConfig

這是由於我們配置RedisConfigure時,使用到了一個ObjectMapper類,這個類需要我們引入Apache的一個工具包

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

最後,我們需要在認證服務器中配置token的存儲方式,還是同jdbc的配置,在tokenStore()方法中配置,打開我們的TokenConfigure類,然後做如下配置:

@Autowired
private RedisConnectionFactory redisConnectionFactory;

@Bean
public TokenStore tokenStore() {
    return new RedisTokenStore(redisConnectionFactory);
}

認證服務器中的配置還是跟之前的一樣,保持不變,啟動項目,獲取Token測試:

我們查看Redis中,看是否已經存入相關信息到數據庫中:

  • 其它

我們打開TokenStore接口的實現類,會發現,還有一種JwkTokenStore,這種實際上就是將我們UUID格式令牌變成可以帶特殊含義的jwt格式的令牌,我們已經在第三篇文章中介紹過了,可以參考前面的文章。

但是我們需要明白一點的是,這種令牌還是存儲在內存中的,後期我們如何將其存儲到redis中是我們研究的方向。

在上面的token存儲到數據庫和存儲到redis中,我們會發現一個問題,那就是我們多次獲取令牌,但是其值是固定不變的,為了解決這個問題,我們可以使用如下方式解決:

@Bean
public TokenStore tokenStore() {
    RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
    // 解決每次生成的 token都一樣的問題
    redisTokenStore.setAuthenticationKeyGenerator(oAuth2Authentication -> UUID.randomUUID().toString());
    return redisTokenStore;
}

使用JWT令牌的實現

在之前的所有使用方法中,我們要麼是將Token存儲在數據庫或者redis中的時候,令牌的格式是UUID的無意義字符串,或者是使用JWT的有意義字符串,但是確是將令牌存儲在內存中,那麼,我們現在想使用JWT令牌的同時,也想將令牌存儲到數據庫或者Redis中。我們該怎麼配置呢?下面我們以Redis存儲為例,進行探索:

首先,我們還是要使用到TokenConfigure中的JWT和Redis配置:

@Autowired
private RedisConnectionFactory redisConnectionFactory;

@Bean
public JwtAccessTokenConverter accessTokenConverter() {
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    //對稱秘鑰,資源服務器使用該秘鑰來驗證
    converter.setSigningKey(SIGNING_KEY);
    return converter;
}

@Bean
public TokenStore tokenStore() {
    return new RedisTokenStore(redisConnectionFactory);
}

接下來,是配置認證服務器,在認證服務器中,跟之前的配置有一點區別,如下:

@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;

@Autowired
private TokenStore tokenStore;

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
    endpoints
            // 配置密碼模式管理器
            .authenticationManager(authenticationManager)
            // 配置授權碼模式管理器
            .authorizationCodeServices(authorizationCodeServices())
            // 令牌管理
//                .tokenServices(tokenServices());
            .accessTokenConverter(jwtAccessTokenConverter)
            .tokenStore(tokenStore);

}

啟動項目,進行測試,獲取token

然後使用該令牌請求資源

至此,我們差不多完成了Token令牌的存儲和獲取

源碼傳送門: https://gitee.com/dimples820/dimples-explore

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

台中搬家公司費用怎麼算?

IEA:再生能源對武漢肺炎適應力最佳 將是滿足2020需求增長的唯一能源

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

「封城新時代」垃圾量爆增! 防疫優先環保擺一邊

摘錄自2020年05月08日TVBS新聞網全球報導

全球進入「封城新時代」,人類減少外出,大大改善了地球環境,不過塑膠污染卻愈來愈嚴重。美國各州,為了防止重複使用的購物袋傳播病毒,開放使用塑膠袋,許多環保措施因此停擺。而宅經濟正夯,網購的貨品包裝,也製造出大量垃圾。

加州塑膠袋全面啟用,當地原先規定購買塑膠袋必須支付10美分,大約台幣3元錢,如今禁令暫停60天,因為衛生安全問題,暫時大於環保問題。就連舊金山,身為全美最先禁用塑膠袋的城市之一,也已經宣布,禁止消費者攜帶自己的環保杯、環保袋等用品到店內。

無論是外出購物,還是網購,似乎怎麼做都會製造塑膠廢棄物,知名零垃圾專家就教大家運用「5R方針」,包括Refuse拒絕垃圾、Reduse減量、Reuse重複使用、Recycle回收,以及Rot把廚餘做成堆肥。零廢棄專家BeaJohnson:「這5R在世界各地都適用,不管你身處什麼情況,包括全球大流行疫情期間也是。」

公害污染
污染治理
國際新聞
美國
塑膠袋
塑膠袋禁用政策
一次性包裝
一次性塑膠袋
廢棄物

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

LG印度工廠化學氣體外洩已11死 村民抬遺體抗議要求關廠

摘錄自2020年5月10日自由時報報導

印度東南部維沙卡帕特南(Visakhapatnam)7日時,南韓LG集團子公司的聚合物工廠發生苯乙烯(styrene)氣體大規模外洩,據外媒,目前已知最少11死,逾千人住院,現仍有數百人在院內接受治療。當地時間週六,憤怒的村民將受害者遺體陳列於工廠大門前,要求工廠即刻關閉,同時逮捕工廠最高管理階層。

這間工廠屬於「南韓樂金聚合物(LG Polymers)」為南韓最大化工企業「LG化學(LG Chem Ltd)」的子公司。同日該公司發表聲明向受害者道歉,聲明也透露,初步調查結果證實,這場悲劇是由苯乙烯儲存槽蒸氣外洩而引起的。

苯乙烯被廣泛用於塑膠及橡膠製品製造,據我國勞動部職業安全衛生署2015「職業性苯乙烯、二苯乙烯中毒認定參考指引」,吸入或食入高濃度苯乙烯可能刺激呼吸道,引起昏睡、頭痛、精神混亂、協調感喪失及意識不清等症狀,若食入或在嘔吐下吸入肺部,可能導致嚴重肺組織損傷,引起肺組織壞死,甚至致死。

公害污染
空氣污染
毒物
污染治理
國際新聞
印度
化學工廠
化學氣體

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※推薦台中搬家公司優質服務,可到府估價

不只陸上生物會感染 科學家擔憂武漢肺炎恐可傳「鯨魚」

摘錄自2020年5月9日自由時報報導

武漢肺炎(新型冠狀病毒疾病,COVID-19)衝擊全球,不僅導致近400萬人染疫,與人類親近的犬、貓,甚至貂和大型貓科動物都有確診案例;專家則警告,雖目前還未有正式的科學期刊證實,但有研究顯示武漢肺炎可感染的潛在生物範圍恐擴及「鯨魚」。

根據加國媒體《Nunatsiaq news 》報導,目前科學界認為其他動物也會感染武漢肺炎的原因在於,牠們呼吸道細胞表面的蛋白質類似武漢肺炎的受體ACE2,當武漢肺炎病毒進入體內時,會經由ACE2進入細胞並開始複製,進而侵入呼吸道,造成感染。日前加利福尼亞大學戴維斯分校的研究人員創建了一份ACE2受體的動物清單,雖目前仍須更多研究佐證,但專家恩威亞(Martin Nweeia)日前在威爾遜國際學者中心的討論會上強調,北極海域的海洋哺乳動物有感染武漢肺炎的可能性。

雖然人類多透過飛沫感染到武漢肺炎,但根據國際文獻指出,人的糞便、尿液也會殘留病毒,當這些含有病毒的廢水排入水中時,有可能導致某些動物感染,雖然病毒可能被海水中的某些物質破壞,但是北極海有多個鹹淡水入口,以及因全球暖化冰川徑流和夏季冰融化增加了淡水系統,可能影響海洋生物以及病毒的存活。

生活環境
生態保育
物種保育
生物多樣性
國際新聞
武漢肺炎
鯨魚
動物與大環境變遷
公共衛生

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

網頁設計最專業,超強功能平台可客製化

搭上末班車去了京東,終於可以做東哥兄弟…

介紹下自己

開篇先簡單介紹一下自己,雙非本科,大三在讀,通信學院物聯網工程專業。這個專業的發展方向大致分為軟、硬件兩種,大二的時候感覺自己更喜歡軟件方面,也就開始學習比較常用的 Java。

到了今年三月,開始投簡歷,投的都是 Java 開發工程師崗,參与春招實習生招聘,想體驗一下筆試、面試的過程,最好能拿到實習offer。

搭上了末班車

春招基本結束了,算是搭上了春招的末班車,被春招支配的恐懼也可以告一段落了,藉此機會梳理一下我的春招歷程。

被恐懼支配

剛開始心態很差,每過一面就會感覺很放鬆,可能會開心一下,這種開心的時候可能就不是很用心的在學習。

然而每掛一面就會非常沮喪,心情很低落,連續幾天都會整理不好心情繼續沉浸在學習中。還會懷疑自己是不是不行,是不是能力不夠,是不是….

心情起起落落,甚至在每面完一場,都會隔幾分鐘就刷新官網,看下流程情況,看下自己是不是掛掉了。焦灼的心,顫抖的手,浮躁的狀態。

完完全全被焦慮和恐懼給支配,沒心思學習,感覺自己整個思路、情緒、狀態都到了一個極點,而且是自己沒辦法突破的極點。

被恐懼支配比恐懼本身恐懼多了,而且後勁十足

我苦苦在尋找一個突破口,但一直沒能找到如何突破。如果找不到這個突破口,接下來的春招之路感覺很難走下去。

我遇到了龍叔

一次偶然在朋友圈看到同學轉發一篇文章,是說面試介紹的 當你面試“自我介紹”還在我是XXX時,看到這篇文章的同學們已經拿到了offer… ,看是關於面試的文章,就點進去看了。

看這篇文章確實補充了我很多盲點,於是我就點進去看了下其他的文章,比如這篇 學會龍叔這套面試秘訣,一套大招帶走面試官 ,發現裏面可以加交流群 和 加作者微信

還看到龍叔給粉絲輔導簡歷,於是我就鼓起勇氣加了龍叔微信,把自己的情況描述了下。抱着試試的態度,沒想到龍叔非常認真的回答了我,而且還在粉絲交流群里給大家說了。

非常幸運的遇到了龍叔,他告訴我,

面試完就不要總是等待結果,面試就像期末考,考完我們都不會再去翻書了。面完了,結果就不在我們考慮範圍之內了,要為下一場準備,也不能寄希望於一個公司,應該把心思放在複習和準備上。時刻保持自己的面試狀態,充滿鬥志、不要灰心。因為 offer 是需要流程的,不是面完就發,多準備,多面,之後就是收割 offer 的事情。天天憂心忡忡的,實在無濟於事,完全是浪費自己的時間。

我聽完犹如醍醐灌頂,受益匪淺,麻溜兒地寫在便簽紙上,貼在眼前,提醒自己。

人總有失意和遇到困難想不通的時候,而這時候能讓我的思想從短路變為通路,非常感謝龍叔。

成功的路總是不平坦的

之後四月份,身邊同學陸陸續續有收到 offer 的,去牛客網每次刷新的時候,也都是喜提校招或者實習 offer的記錄帖,羡慕、恰檸檬之餘。

我為自己還沒有理想的offer 感到發愁,又到了浮躁的一個新階段。這個階段雖說是浮躁,但是比起剛開始那一堵牆,已經好很多了。

會和朋友相互鼓勵,相互吐槽失敗的面試,心情 down 的時候聽聽大張偉的《陽光彩虹小白馬》 “你就是最強噠最棒噠最亮噠最發光噠”,努力讓自己平和、快樂,強行相信自己。很快就能調整好自己的狀態,繼續投入到戰鬥中。

心態太重要了,只有心態好了,複習才能更加有效率

這兩個月的面試中,讓我自己印象深刻的是騰訊三面,可能是傳說中的壓力面之類的。

當時操作系統學的不好,說明了之後,面試官還是在操作系統這方面窮追不舍地發問,從一開始的語氣溫柔、帶着笑意,到後來漸漸嚴肅、帶着凶意,與此同時我也意識到這最後一輪技術面多半是涼透了,要和我 say byebye了。

最後面試官甚至問 你到底有沒有學過操作系統?你是女生,為什麼要學開發?

當時的面試,我沒控制住情緒,為自己的菜流下了委屈的淚水

其實也沒什麼委屈的,畢竟菜是原罪,哈哈哈。可能因為人生第一回總監面,沒見過這種大場面的原因吧,還是要見多識廣啊。

事後反思,這樣實在是不合適,這是頂不住壓力的表現,面試官希望看到的,應該是沉着冷靜的,嘗試去解決問題的求職者,而不是這樣愛哭鼻子的。

一些總結

經歷這近三個月的面試,從開始自我介紹都結巴,到現在可以心跳正常地和面試官交流,收穫還是蠻多的。

我感到實力才是硬道理,結果的決定權在公司手裡,作為求職者,我們總是會被置於與其他同樣水平的人作比較的地位,只能不斷提高實力,才可能脫穎而出。

保持平和的心態會帶來一些好的運氣,還有就是堅持下去,最後一定會收穫好結果的。

從簡歷篩選、筆試、輪輪面試,一步一步過關,每次面試過程會錄音,之後復盤,通過復盤去看自己當時為什麼沒有回答上來,為什麼沒有收到面試官的青睞。

通過復盤,把不會的問題都搞明白,把該加分沒加上的,在後續的面試一定加上。面試完需要儘快查漏補缺,保持心態,堅持下去。

春季實習招聘還是比較寬容的,大廠也沒有因為我學歷不出色而不給面試機會,而且很多家的面試體驗還是很不錯的,有的面試官會引導我、會糾正我的錯誤、給予建議。

即使最後沒有通過,也是學到了一些東西,面試本身就是一種學習。

面試最好的狀態是和面試官交流,而不是硬生的回答。

最後希望秋招時,我可以擁有更平和的心態和更紮實的基礎,收穫自己心儀的offer~ ,也希望和我一起奮鬥的你們都能找到滿意的offer。

高頻考點

這裏列出遇到的面試中高頻的考點(被問到三次以上的那種~):

  • Java 基礎:HashMap源碼、泛型、NIO
  • 數據結構與算法:紅黑樹、堆、 海量數據中找top k 問題、 快速排序、堆排序
  • JVM :垃圾回收機制、Full GC、類加載機制
  • 數據庫:事務、索引、鎖、查詢優化、排查慢查詢
  • Spring 框架:IOC、AOP、事務、SpringMVC、常見註解
  • 操作系統:進程和線程、虛擬內存
  • 網絡:HTTPS、TCP三次握手四次揮手、HTTP狀態碼
  • 手撕算法:基本都是劍指offer上面的原題,還有 生產者-消費者模型
  • 再有,如果有讀過併發包中的源碼,或者對線程安全相關問題有自己的思考,也是很加分的。

這些是非常高頻的面試題,還有一些常規的,就不一一列舉了。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

※別再煩惱如何寫文案,掌握八大原則!

分佈式鎖沒那麼難,手把手教你實現 Redis 分佈鎖!|保姆級教程

書接上文

上篇文章「MySQL 可重複讀,差點就讓我背上了一個 P0 事故!」發布之後,收到很多小夥伴們的留言,從中又學習到很多,總結一下。

上篇文章可能舉得例子有點不恰當,導致有些小夥伴沒看懂為什麼餘額會變負。

這次我們舉得實際一點,還是上篇文章 account 表,假設 id=1,balance=1000,不過這次我們扣款 1000,兩個事務的時序圖如下:

這次使用兩個命令窗口真實執行一把:

注意事務 2,③處查詢到 id=1,balance=1000,但是實際上由於此時事務 1 已經提交,最新結果如②處所示 id=1,balance=900

本來 Java 代碼層會做一層餘額判斷:

if (balance - amount < 0) {
  throw new XXException("餘額不足,扣減失敗");
}

但是此時由於 ③ 處使用快照讀,讀到是箇舊值,未讀到最新值,導致這層校驗失效,從而代碼繼續往下運行,執行了數據更新。

更新語句又採用如下寫法:

UPDATE account set balance=balance-1000 WHERE id =1;

這條更新語句又必須是在這條記錄的最新值的基礎做更新,更新語句執行結束,這條記錄就變成了 id=1,balance=-1000

之前有朋友疑惑 t12 更新之後,再次進行快照讀,結果會是多少。

上圖執行結果 ④ 可以看到結果為 id=1,balance=-1000,可以看到已經查詢最新的結果記錄。

這行數據最新版本由於是事務 2 自己更新的,自身事務更新永遠對自己可見

另外這次問題上本質上因為 Java 層與數據庫層數據不一致導致,有的朋友留言提出,可以在更新餘額時加一層判斷:

UPDATE account set balance=balance-1000 WHERE id =1 and balance>0;

然後更新完成,Java 層判斷更新有效行數是否大於 0。這種做法確實能規避這個問題。

最後這位朋友留言總結的挺好,粘貼一下:

先贊后看,微信搜索「程序通事」,關注就完事了

手擼分佈式鎖

現在切回正文,這篇文章本來是準備寫下 Mysql 查詢左匹配的問題,但是還沒研究出來。那就先寫下最近在鼓搗一個東西,使用 Redis 實現可重入分佈鎖。

看到這裏,有的朋友可能會提出來使用 redisson 不香嗎,為什麼還要自己實現?

哎,redisson 真的很香,但是現有項目中沒辦法使用,只好自己手擼一個可重入的分佈式鎖了。

雖然用不了 redisson,但是我可以研究其源碼,最後實現的可重入分佈鎖參考了 redisson 實現方式。

分佈式鎖

分佈式鎖特性就要在於排他性,同一時間內多個調用方加鎖競爭,只能有一個調用方加鎖成功。

Redis 由於內部單線程的執行,內部按照請求先後順序執行,沒有併發衝突,所以只會有一個調用方才會成功獲取鎖。

而且 Redis 基於內存操作,加解鎖速度性能高,另外我們還可以使用集群部署增強 Redis 可用性。

加鎖

使用 Redis 實現一個簡單的分佈式鎖,非常簡單,可以直接使用 SETNX 命令。

SETNX 是『SET if Not eXists』,如果不存在,才會設置,使用方法如下:

不過直接使用 SETNX 有一個缺陷,我們沒辦法對其設置過期時間,如果加鎖客戶端宕機了,這就導致這把鎖獲取不了了。

有的同學可能會提出,執行 SETNX 之後,再執行 EXPIRE 命令,主動設置過期時間,偽碼如下:

var result = setnx lock "client"
if(result==1){
    // 有效期 30 s
    expire lock 30
}

不過這樣還是存在缺陷,加鎖代碼並不能原子執行,如果調用加鎖語句,還沒來得及設置過期時間,應用就宕機了,還是會存在鎖過期不了的問題。

不過這個問題在 Redis 2.6.12 版本 就可以被完美解決。這個版本增強了 SET 命令,可以通過帶上 NX,EX 命令原子執行加鎖操作,解決上述問題。參數含義如下:

  • EX second :設置鍵的過期時間,單位為秒
  • NX 當鍵不存在時,進行設置操作,等同與 SETNX 操作

使用 SET 命令實現分佈式鎖只需要一行代碼:

SET lock_name anystring NX EX lock_time

解鎖

解鎖相比加鎖過程,就顯得非常簡單,只要調用 DEL 命令刪除鎖即可:

DEL lock_name

不過這種方式卻存在一個缺陷,可能會發生錯解鎖問題。

假設應用 1 加鎖成功,鎖超時時間為 30s。由於應用 1 業務邏輯執行時間過長,30 s 之後,鎖過期自動釋放。

這時應用 2 接着加鎖,加鎖成功,執行業務邏輯。這個期間,應用 1 終於執行結束,使用 DEL 成功釋放鎖。

這樣就導致了應用 1 錯誤釋放應用 2 的鎖,另外鎖被釋放之後,其他應用可能再次加鎖成功,這就可能導致業務重複執行。

為了使鎖不被錯誤釋放,我們需要在加鎖時設置隨機字符串,比如 UUID。

SET lock_name uuid NX EX lock_time

釋放鎖時,需要提前獲取當前鎖存儲的值,然後與加鎖時的 uuid 做比較,偽代碼如下:

var value= get lock_name
if value == uuid
	// 釋放鎖成功
else
	// 釋放鎖失敗

上述代碼我們不能通過 Java 代碼運行,因為無法保證上述代碼原子化執行。

幸好 Redis 2.6.0 增加執行 Lua 腳本的功能,lua 代碼可以運行在 Redis 服務器的上下文中,並且整個操作將會被當成一個整體執行,中間不會被其他命令插入。

這就保證了腳本將會以原子性的方式執行,當某個腳本正在運行的時候,不會有其他腳本或 Redis 命令被執行。在其他的別的客戶端看來,執行腳本的效果,要麼是不可見的,要麼就是已完成的。

EVAL 與 EVALSHA

EVAL

Redis 可以使用 EVAL 執行 LUA 腳本,而我們可以在 LUA 腳本中執行判斷求值邏輯。EVAL 執行方式如下:

EVAL script numkeys key [key ...] arg [arg ...]

numkeys 參數用於建明參數,即後面 key 數組的個數。

key [key ...] 代表需要在腳本中用到的所有 Redis key,在 Lua 腳本使用使用數組的方式訪問 key,類似如下 KEYS[1]KEYS[2]。注意 Lua 數組起始位置與 Java 不同,Lua 數組是從 1 開始。

命令最後,是一些附加參數,可以用來當做 Redis Key 值存儲的 Value 值,使用方式如 KEYS 變量一樣,類似如下:ARGV[1]ARGV[2]

用一個簡單例子運行一下 EVAL 命令:

eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2],ARGV[3]}" 2 key1 key2 first second third

運行效果如下:

可以看到 KEYSARGVS內部數組可以不一致。

在 Lua 腳本可以使用下面兩個函數執行 Redis 命令:

  • redis.call()
  • redis.pcall()

兩個函數作用法與作用完全一致,只不過對於錯誤的處理方式不一致,感興趣的小夥伴可以具體點擊以下鏈接,查看錯誤處理一章。

http://doc.redisfans.com/script/eval.html

下面我們統一在 Lua 腳本中使用 redis.call(),執行以下命令:

eval "return redis.call('set',KEYS[1],ARGV[1])" 1 foo 樓下小黑哥

運行效果如下:

EVALSHA

EVAL 命令每次執行時都需要發送 Lua 腳本,但是 Redis 並不會每次都會重新編譯腳本。

當 Redis 第一次收到 Lua 腳本時,首先將會對 Lua 腳本進行 sha1 獲取簽名值,然後內部將會對其緩存起來。後續執行時,直接通過 sha1 計算過後簽名值查找已經編譯過的腳本,加快執行速度。

雖然 Redis 內部已經優化執行的速度,但是每次都需要發送腳本,還是有網絡傳輸的成本,如果腳本很大,這其中花在網絡傳輸的時間就會相應的增加。

所以 Redis 又實現了 EVALSHA 命令,原理與 EVAL 一致。只不過 EVALSHA 只需要傳入腳本經過 sha1計算過後的簽名值即可,這樣大大的減少了傳輸的字節大小,減少了網絡耗時。

EVALSHA命令如下:

evalsha c686f316aaf1eb01d5a4de1b0b63cd233010e63d 1 foo 樓下小黑哥

運行效果如下:

SCRIPT FLUSH 命令用來清除所有 Lua 腳本緩存。

可以看到,如果之前未執行過 EVAL命令,直接執行 EVALSHA 將會報錯。

優化執行 EVAL

我們可以結合使用 EVALEVALSHA,優化程序。下面就不寫偽碼了,以 Jedis 為例,優化代碼如下:

//連接本地的 Redis 服務
Jedis jedis = new Jedis("localhost", 6379);
jedis.auth("1234qwer");

System.out.println("服務正在運行: " + jedis.ping());

String lua_script = "return redis.call('set',KEYS[1],ARGV[1])";
String lua_sha1 = DigestUtils.sha1DigestAsHex(lua_script);

try {
    Object evalsha = jedis.evalsha(lua_sha1, Lists.newArrayList("foo"), Lists.newArrayList("樓下小黑哥"));
} catch (Exception e) {
    Throwable current = e;
    while (current != null) {
        String exMessage = current.getMessage();
        // 包含 NOSCRIPT,代表該 lua 腳本從未被執行,需要先執行 eval 命令
        if (exMessage != null && exMessage.contains("NOSCRIPT")) {
            Object eval = jedis.eval(lua_script, Lists.newArrayList("foo"), Lists.newArrayList("樓下小黑哥"));
            break;
        }

    }
}
String foo = jedis.get("foo");
System.out.println(foo);

上面的代碼看起來還是很複雜吧,不過這是使用原生 jedis 的情況下。如果我們使用 Spring Boot 的話,那就沒這麼麻煩了。Spring 組件執行的 Eval 方法內部就包含上述代碼的邏輯。

不過需要注意的是,如果 Spring-Boot 使用 Jedis 作為連接客戶端,並且使用Redis Cluster 集群模式,需要使用 2.1.9 以上版本的spring-boot-starter-data-redis,不然執行過程中將會拋出:

org.springframework.dao.InvalidDataAccessApiUsageException: EvalSha is not supported in cluster environment.

詳細情況可以參考這個修復的 IssueAdd support for scripting commands with Jedis Cluster

優化分佈式鎖

講完 Redis 執行 LUA 腳本的相關命令,我們來看下如何優化上面的分佈式鎖,使其無法釋放其他應用加的鎖。

以下代碼基於 spring-boot 2.2.7.RELEASE 版本,Redis 底層連接使用 Jedis。

加鎖的 Redis 命令如下:

SET lock_name uuid NX EX lock_time

加鎖代碼如下:

/**
 * 非阻塞式加鎖,若鎖存在,直接返回
 *
 * @param lockName  鎖名稱
 * @param request   唯一標識,防止其他應用/線程解鎖,可以使用 UUID 生成
 * @param leaseTime 超時時間
 * @param unit      時間單位
 * @return
 */
public Boolean tryLock(String lockName, String request, long leaseTime, TimeUnit unit) {
    // 注意該方法是在 spring-boot-starter-data-redis 2.1 版本新增加的,若是之前版本 可以執行下面的方法
    return stringRedisTemplate.opsForValue().setIfAbsent(lockName, request, leaseTime, unit);
}

由於setIfAbsent方法是在 spring-boot-starter-data-redis 2.1 版本新增加,之前版本無法設置超時時間。如果使用之前的版本的,需要如下方法:

/**
 * 適用於 spring-boot-starter-data-redis 2.1 之前的版本
 *
 * @param lockName
 * @param request
 * @param leaseTime
 * @param unit
 * @return
 */
public Boolean doOldTryLock(String lockName, String request, long leaseTime, TimeUnit unit) {
    Boolean result = stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> {
        RedisSerializer valueSerializer = stringRedisTemplate.getValueSerializer();
        RedisSerializer keySerializer = stringRedisTemplate.getKeySerializer();

        Boolean innerResult = connection.set(keySerializer.serialize(lockName),
                valueSerializer.serialize(request),
                Expiration.from(leaseTime, unit),
                RedisStringCommands.SetOption.SET_IF_ABSENT
        );
        return innerResult;
    });
    return result;
}

解鎖需要使用 Lua 腳本:

-- 解鎖代碼
-- 首先判斷傳入的唯一標識是否與現有標識一致
-- 如果一致,釋放這個鎖,否則直接返回
if redis.call('get', KEYS[1]) == ARGV[1] then
   return redis.call('del', KEYS[1])
else
   return 0
end

這段腳本將會判斷傳入的唯一標識是否與 Redis 存儲的標示一致,如果一直,釋放該鎖,否則立刻返回。

釋放鎖的方法如下:

/**
 * 解鎖
 * 如果傳入應用標識與之前加鎖一致,解鎖成功
 * 否則直接返回
 * @param lockName 鎖
 * @param request 唯一標識
 * @return
 */
public Boolean unlock(String lockName, String request) {
    DefaultRedisScript<Boolean> unlockScript = new DefaultRedisScript<>();
    unlockScript.setLocation(new ClassPathResource("simple_unlock.lua"));
    unlockScript.setResultType(Boolean.class);
    return stringRedisTemplate.execute(unlockScript, Lists.newArrayList(lockName), request);
}

由於公號外鏈無法直接跳轉,關注『程序通事』,回復分佈式鎖獲取源代碼。

Redis 分佈式鎖的缺陷

無法重入

由於上述加鎖命令使用了 SETNX ,一旦鍵存在就無法再設置成功,這就導致後續同一線程內繼續加鎖,將會加鎖失敗。

如果想將 Redis 分佈式鎖改造成可重入的分佈式鎖,有兩種方案:

  • 本地應用使用 ThreadLocal 進行重入次數計數,加鎖時加 1,解鎖時減 1,當計數變為 0 釋放鎖
  • 第二種,使用 Redis Hash 表存儲可重入次數,使用 Lua 腳本加鎖/解鎖

第一種方案可以參考這篇文章分佈式鎖的實現之 redis 篇。第二個解決方案,下一篇文章就會具體來聊聊,敬請期待。

鎖超時釋放

假設線程 A 加鎖成功,鎖超時時間為 30s。由於線程 A 內部業務邏輯執行時間過長,30s 之後鎖過期自動釋放。

此時線程 B 成功獲取到鎖,進入執行內部業務邏輯。此時線程 A 還在執行執行業務,而線程 B 又進入執行這段業務邏輯,這就導致業務邏輯重複被執行。

這個問題我覺得,一般由於鎖的超時時間設置不當引起,可以評估下業務邏輯執行時間,在這基礎上再延長一下超時時間。

如果超時時間設置合理,但是業務邏輯還有偶發的超時,個人覺得需要排查下業務執行過長的問題。

如果說一定要做到業務執行期間,鎖只能被一個線程佔有的,那就需要增加一個守護線程,定時為即將的過期的但未釋放的鎖增加有效時間。

加鎖成功后,同時創建一個守護線程。守護線程將會定時查看鎖是否即將到期,如果鎖即將過期,那就執行 EXPIRE 等命令重新設置過期時間。

說實話,如果要這麼做,真的挺複雜的,感興趣的話可以參考下 redisson watchdog 實現方式。

Redis 分佈式鎖集群問題

為了保證生產高可用,一般我們會採用主從部署方式。採用這種方式,我們可以將讀寫分離,主節點提供寫服務,從節點提供讀服務。

Redis 主從之間數據同步採用異步複製方式,主節點寫入成功后,立刻返回給客戶端,然後異步複製給從節點。

如果數據寫入主節點成功,但是還未複製給從節點。此時主節點掛了,從節點立刻被提升為主節點。

這種情況下,還未同步的數據就丟失了,其他線程又可以被加鎖了。

針對這種情況, Redis 官方提出一種 RedLock 的算法,需要有 N 個Redis 主從節點,解決該問題,詳情參考:

https://redis.io/topics/distlock。

這個算法自己實現還是很複雜的,幸好 redisson 已經實現的 RedLock,詳情參考:redisson redlock

總結

本來這篇文章是想寫 Redis 可重入分佈式鎖的,可是沒想到寫分佈式鎖的實現方案就已經寫了這麼多,再寫下去,文章可能就很長,所以拆分成兩篇來寫。

嘿嘿,這不下星期不用想些什麼了,真是個小機靈鬼~

好了,幫大家再次總結一下本文內容。

簡單的 Redis 分佈式鎖的實現方式還是很簡單的,我們可以直接用 SETNX/DEL 命令實現加解鎖。

不過這種實現方式不夠健壯,可能存在應用宕機,鎖就無法被釋放的問題。

所以我們接着引入以下命令以及 Lua 腳本增強 Redis 分佈式鎖。

SET lock_name anystring NX EX lock_time

最後 Redis 分佈鎖還是存在一些缺陷,在這裏提出一些解決方案,感興趣同學可以自己實現一下。

下篇文章再來將將 Redis 可重入分佈式鎖~

參考資料

  1. 分佈式鎖的實現之 redis 篇
  2. 基於 Redis 的分佈式鎖

歡迎關注我的公眾號:程序通事,獲得日常乾貨推送。如果您對我的專題內容感興趣,也可以關注我的博客:studyidea.cn

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!

基於Azure IoT開發.NET物聯網應用系列-全新的Azure IoT架構

物聯網技術已經火了很多年了,業界各大廠商都有各自成熟的解決方案。我們公司主要搞新能源汽車充電,充電樁就是物聯網技術的最大應用,車聯網、物聯網、互聯網三網合一。2017年的時候重點研究過Azure IoT技術架構和使用,

Azure IoT 技術研究系列1-入門篇

隨着業界技術的發展,近期又重新關注並研究了最新的Azure  IoT架構,現在將結合著.NET Core技術和Azure IoT 做一些物聯網應用,將研究的成果分享給大家。

關於IoT的一些基本概念,重新梳理一下,分享給大家:

  • IoT:Internet of Things,即萬物互聯。
  • IoT Devices:物聯網設備。
  • IoT Edge Devices:物聯網邊緣計算設備。
  • IoT Gateway:IoT網關,負責IoT物聯網設備的接入、管理和控制、通訊(上行和下行)
  • 通訊協議:TCP、MQTT、AMQP、HTTPS、zgebee等等
  • Azure IoT Central。 IoT Central 是完全託管的 IoT物聯網 SaaS(軟件即服務)服務
  • 目前Azure仍然提供了Azure IoT Hub:直譯為Azure的物聯網中心
  • Azure IoT Hub為物聯網設備提供註冊、管理、溝通交互的雲服務。可用於管理數十億物聯網設備,提供可靠和安全的雲端與設備之間的雙向通信支持,每月可處理數以萬億計消息,並簡化了與其他Azure服務之間的集成,包括Azure機器學習以及
  • Azure流分析等。它是微軟Azure IoT Suite的重要組成部分,也是微軟物聯網戰略的重要基礎。

接下來,我們看一下Azure IoT最新的技術架構:

   

   下面,我們詳細介紹一下這個架構組成:

   一、 Things(物聯網設備側)

  1. IoT devices:前面已經介紹過了,泛指各類物聯網設備。設備可以安全地註冊到雲中,並且可以連接到雲之後,發送和接收數據。

  2. IoT edge devices:物聯網邊緣計算設備,某些設備可能會是在設備本身上或在現場網關中執行一些數據處理的邊緣設備。舉個大家平時常見的設備:充電樁,作為IoT邊緣計算設備,其自身有嵌入式操作系統、AI智能芯片,可以實現一些簡單的邊緣計算場景

  3. Cloud Gateway:雲網關,雲網關提供一個雲中心,以便設備安全地連接到雲併發送數據。 它還提供設備管理功能,包括設備的命令和控制。

      對於雲網關,Azure 建議使用Azure  IoT 中心。Azure IoT 中心是從設備引入事件的託管雲服務,充當設備與後端服務之間的消息代理。 同時提供安全連接、事件引入、雙向通信和設備管理。

      當然,我們也可以自建雲網關,支持各類物聯網設備的接入、管理和控制。

  4. Bulk devices provisioning:設備批量設置,統一管理設置海量設備。 對於註冊和連接許多組設備。可以使用 IoT 中心設備預配服務 (DPS)。 DPS 可用於大規模分配設備並將設備註冊到特定 Azure IoT 中心終結點。

二、Insights(洞察、洞見,可以理解為設備接入管理、數據處理、數據持久化、數據分析、可視化)

  1. Streaming Processing:流式數據處理

  Azure提供了專門的流分析服務。 流分析可以使用時間開窗函數、流聚合和外部數據源聯接大規模執行複雜分析。假如說我們自建系統做物聯網數據流式分析的話,可以使用Kafka、Flink、Spark等主流的大數據流式分析技術。

  2. Data transformation:數據轉換操作或聚合遙測數據流。

  常見的場景包括通訊協議轉換,例如,將二進制數據轉換為 JSON,或者合併數據點。 如果數據在到達 IoT 中心之前必須轉換,可以使用協議網關(一個可以轉換數據的網關)。 同時,數據可以在到達 IoT 中心後轉換。

  在這種情況下,可以使用 Azure Functions 函數計算,Azure Functions內置了與 IoT 中心、Cosmos DB 和 Blob 存儲的集成。

  3. Warm path store:熱存儲

  熱存儲,存儲實時物聯網設備上傳下發的數據,這些數據必須可按設備實時查詢,以用於報告和可視化。舉個實際的業務場景:充電樁實時上傳的電壓、電流、SOC等實時設備數據,這些數據的實時性要求高,可以存儲在熱存儲中。

  4. Cold path store:冷存儲

  如果所有的物聯網設備數據全部存儲在熱存儲中,其硬件成本會很高。數據具備一定的時效性,因為,當數據失去了一定的時效性要求后,可以存儲在冷存儲中,降低存儲的成本。

  這些數據會保留較長時間,用於批處理。 對於冷路徑存儲,可以使用 Azure Blob 存儲。 數據可無限期地以較低成本在 Blob 存儲中存檔,並且可以輕鬆訪問以進行批處理。

  5. UI Reporting and tools:可視化展現

  可視化展現方面,通常包含:IoT設備管理UI、設備控制UI、趨勢圖、連接狀態圖表、數據分析圖表等等,這個地方可以使用各類UI展現技術實現了。

三、 Action(運維管理、操作)

  1. Machine Learning:機器學習

   大家會問,用機器學習干什麼?通過歷史遙測數據執行模型訓練,實現IoT設備的預測性維護,同時還能做什麼?還可以對上報的數據建立不同的模型,實時進行訓練,智能控制設備。比如說充電樁的例子,動態調控充電功率,實現最大充電效率。

  2. Business integration:業務流程集成

   業務流程集成根據來自設備數據執行各類後續操作。 可以包括:存儲實時消息、引發警報、發送电子郵件或短信,或者與 CRM 集成。舉個實際的業務場景:當需要設備運維時,發出一個運維工單到產品運維部門,實現IoT設備的智能運維和派單處理。

  3. User Management:用戶管理

   用戶管理限制哪些用戶或組可以在設備上執行操作,例如升級固件。 它還定義應用程序中的用戶功能。

  綜上是Azure IoT架構的詳細介紹和說明,比2017年時,產品更加SaaS化,更加AI智能、更加體系。分享給大家。

 

 

周國慶

2020/6/7

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!

ASP.NET Core通過Nacos SDK讀取阿里雲ACM

背景

前段時間,cranelee 在Github上給老黃提了個issues, 問到了如何用Nacos的SDK訪問阿里雲ACM。

https://github.com/catcherwong/nacos-sdk-csharp/issues/13

剛看到這個issues的時候,老黃也是覺得一臉懵逼,好像這兩者沒有什麼必然聯繫,打開ACM的文檔一看,就知道為什麼了。

原來Java和Go的已經是可以用nacos的SDK來訪問的了。那就說明兩者是兼容的。

這段時間抽空看了一下,把這個功能基本實現了。

下面就簡單介紹一下。

簡單看看ACM

開通ACM之後,可以看到類似這樣的界面。其實和Nacos控制台的配置部分差不遠。

要使用這個的話,需要幾個東西,一個是ACM上面的命名空間,一個是AccessKey ID,一個是AccessKey Secret。

其中的AK/SK可以在命名空間詳情裏面獲取。

然後就是添加配置了。

三張圖,看個大概就好了,下面來具體看看在.NET Core中怎麼使用。

如何使用

安裝最新預覽版的SDK

<ItemGroup>
    <PackageReference Include="nacos-sdk-csharp-unofficial.Extensions.Configuration" Version="0.2.7-alpha7" />
</ItemGroup>

注:目前還沒有發布正式版,不過不影響正常使用了。

修改Program

public class Program
{
    public static void Main(string[] args)
    {
        // 處理編碼問題
        System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((context, builder) =>
            {
                // 這兩行代碼就是關鍵
                var c = builder.Build();
                builder.AddNacosConfiguration(c.GetSection("NacosConfig"));
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

調整appsettings.json

{
  "NacosConfig": {
    "Optional": false,
    "DataId": "msconfigapp",
    "Group": "",
    "Tenant": "<換成您的命名空間>",
    "ServerAddresses": [],
    "AccessKey": "<換成您的AK>",
    "SecretKey": "<換成您的SK>",
    "EndPoint": "acm.aliyun.com"
  }
}

注: 由於老黃開通個人開通的,沒有內網服務器,所以用的是公網的EndPoint,這個需要根據情況自行調整。

實體映射(非必須)

public class AppSettings
{
    public string Str { get; set; }

    public int Num { get; set; }

    public List<int> Arr { get; set; }

    public SubObj SubObj { get; set; }
}

public class SubObj
{
    public string a { get; set; }
}

為了方便和配置一一對應,可以建立實體,做一個映射。

加了這個的,需要在Startup上面配置一下。

public void ConfigureServices(IServiceCollection services)
{   
    // others ...
    
    services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
}

讀取配置

這裏用控制器做為示例

[ApiController]
[Route("api/[controller]")]
public class ConfigController : ControllerBase
{
    private readonly ILogger<ConfigController> _logger;
    private readonly IConfiguration _configuration;
    private readonly AppSettings _settings;
    private readonly AppSettings _sSettings;
    private readonly AppSettings _mSettings;

    public ConfigController(
        ILogger<ConfigController> logger,
        IConfiguration configuration,
        IOptions<AppSettings> options,
        IOptionsSnapshot<AppSettings> sOptions,
        IOptionsMonitor<AppSettings> _mOptions
        )
    {
        _logger = logger;
        _configuration = configuration;
        _settings = options.Value;
        _sSettings = sOptions.Value;
        _mSettings = _mOptions.CurrentValue;
    }

    [HttpGet]
    public string Get()
    {
        string id = Guid.NewGuid().ToString("N");

        _logger.LogInformation($"============== begin {id} =====================");

        var conn = _configuration.GetConnectionString("Default");
        _logger.LogInformation($"{id} conn = {conn}");

        var version = _configuration["version"];
        _logger.LogInformation($"{id} version = {version}");

        var str1 = Newtonsoft.Json.JsonConvert.SerializeObject(_settings);
        _logger.LogInformation($"{id} IOptions = {str1}");

        var str2 = Newtonsoft.Json.JsonConvert.SerializeObject(_sSettings);
        _logger.LogInformation($"{id} IOptionsSnapshot = {str2}");

        var str3 = Newtonsoft.Json.JsonConvert.SerializeObject(_mSettings);
        _logger.LogInformation($"{id} IOptionsMonitor = {str3}");

        _logger.LogInformation($"===============================================");
        _logger.LogInformation($"===============================================");
        _logger.LogInformation($"===============================================");

        return "ok";
    }
}

附上一張操作動圖

在ACM上修改之後,程序是可以馬上讀取到的。

下面是本文的示例代碼。

https://github.com/catcherwong-archive/2020/tree/master/06/NacosACMDemo

小結

Nacos和ACM的操作基本都是一致的,比較不一樣的地方是,從直連Nacos變成要先去地址服務拿到Nacos的地址后再操作。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

※回頭車貨運收費標準