Ocelot+Consul 集群搭建實踐


博客園已經有很多大神寫過consul集群搭建了。大家都在玩,那我也不能托后退呢

不過自己研究下還是好的。畢竟每個人遇到的問題的不同

研究過才能說自己玩過consul,文章有部分名詞解釋是收集網絡

Consul 官網:https://www.consul.io/

幫助文檔:https://www.consul.io/docs/upgrading.html

Consul是一個服務網格(微服務間的 TCP/IP,負責服務之間的網絡調用、限流、熔斷和監控)解決方案,它是一個一個分布式的,高度可用的系統,
而且開發使用都很簡便。它提供了一個功能齊全的控制平面,主要特點是:服務發現、健康檢查、鍵值存儲、安全服務通信、多數據中心。

 

具體的理論在網上都能找到,我這里就不描述了。主要寫部署的實踐過程

 

 

我看了網上大部分都是通過一台機器來模擬的集群,我打算通過多台服務器來進行集群搭建

這樣才有實戰效果,因為官方推薦用3台做server,所以用3台server只部署server,不部署其他程序

conusl集群通信中有很多端口:

8300:TCP # Server RPC :agent server 使用的,用於處理其他agent發來的請求
8301:TCP and UDP # Serf LAN: 所有的agent都要使用這個端口,用於處理LAN中的gossip,
8302:TCP and UDP # Serf WAN:agent Server使用的,處理WAN中的與其他server的gossip
8400:TCP # 所有agent都要使用的端口,用於處理從CLI來的RPC。
8500:TCP # 所有agent都要使用的端口,用於處理HTTP API。
8600:TCP and UDP # 用於處理 DNS 查詢。

 所以需要開通的端口有:8301/tcp 8301/udp 8302/tcp  8302/udp 8300/tcp 8500/tcp  8600/udp   如果我寫漏了。自己補上

如果是阿里雲服務,需要登陸阿里雲配置本地安全組

一些相關命令:

firewall-cmd --list-ports #查看已開放的端口
firewall-cmd --reload # 重啟防火牆,剛開通的端口才會生效
firewall-cmd --query-port=80/tcp #查詢80端口開通狀態
firewall-cmd --add-port=80/udp --permanent   #開通80端口udp協議

 

名詞概念

client:

    CLIENT表示consul的client模式,就是客戶端模式。是consul節點的一種模式,這種模式下,所有注冊到當前節點的服務會被轉發到SERVER,本身是不持久化這些信息。

server:

       SERVER表示consul的server模式,表明這個consul是個server,這種模式下,功能和CLIENT都一樣,唯一不同的是,它會把所有的信息持久化的本地,這樣遇到故障,信息是可以被保留的。

server-leader:

      中間那個SERVER下面有LEADER的字眼,表明這個SERVER是它們的老大,它和其它SERVER不一樣的一點是,它需要負責同步注冊的信息給其它的SERVER,同時也要負責各個節點的健康監測。

raft:

   server節點之間的數據一致性保證,一致性協議使用的是raft,而zookeeper用的paxos,etcd采用的也是taft。

    服務發現協議: consul采用http和dns協議,etcd只支持http

    服務注冊: consul支持兩種方式實現服務注冊,一種是通過consul的服務注冊http API,由服務自己調用API實現注冊,另一種方式是通過json個是的配置文件實現注冊,將需要注冊的服務以json格式的配置文件給出。consul官方建議使用第二種方式。

    服務發現: consul支持兩種方式實現服務發現,一種是通過http API來查詢有哪些服務,另外一種是通過consul agent 自帶的DNS(8600端口),域名是以NAME.service.consul的形式給出,NAME即在定義的服務配置文件中,服務的名稱。DNS方式可以通過check的方式檢查服務。

    服務間的通信協議: Consul使用gossip協議管理成員關系、廣播消息到整個集群,他有兩個gossip pool(LAN pool和WAN pool),LAN pool是同一個數據中心內部通信的,WAN pool是多個數據中心通信的,LAN pool有多個,WAN pool只有一個。

    LAN Gossip——它包含所有位於同一個局域網或者數據中心的所有節點。

    WAN Gossip——它只包含Server。這些server主要分布在不同的數據中心並且通常通過因特網或者廣域網通信。

    RPC——遠程過程調用。這是一個允許client請求server的請求/響應機制。

 

簡單來說就是:client相當於我們平時說的LB,負責將請求轉發到Server,Server中有一個leader,負責Server集群的同步和監測,
這個server-leader在不指定的情況下回隨機推舉出一個,當然也可以手動指定。這個在ACL配置的時候需要保證Server-leader是同一個。

 

參數解釋

1、命令參數

-advertise:通知展現地址用來改變我們給集群中的其他節點展現的地址,默認情況下-bind地址就是展現地址,然而也存在一些路由地址是不能受約束的,這時候會激活一個不同的地址來供應,如果這個地址不能路由,這個路由將不能被加入集群

-bootstrap:用來控制一個server是否在bootstrap模式,在一個datacenter中只能有一個server處於bootstrap模式,當一個server處於bootstrap模式時,可以自己選舉為raft leader,在一個節點的模式下這種方式很重要,否則在集群中的一致性不能保證,不推薦在集群中應用這個標識

-bootstrap-expect:在一個datacenter中期望提供的server節點數目,當該值提供的時候,consul一直等到達到指定sever數目的時候才會引導整個集群,該標記不能和bootstrap公用(推薦使用的方式)

-bind:該地址用來在集群內部的通訊,集群內的所有節點到地址都必須是可達的,默認是0.0.0.0,這意味着Consulo會使用第一個可用的私有IP地址,Consul可以使用TCP和UDP並且可以使用共同的端口,如果存在防火牆,這兩者協議必須是允許的。

-client:consul綁定在哪個client地址上,這個地址提供HTTP、DNS、RPC等服務,默認是127.0.0.1,只允許回路連接。RPC地址用於Consul命令,比如Consul members可以查詢當前運行的Consul代理

-config-file:明確的指定要加載哪個配置文件,文件下的所有配置會合並在一起進行加載

-config-dir:配置文件目錄,里面所有以.json結尾的文件都會被加載

-data-dir:提供一個目錄用來存放agent的狀態,所有的agent允許都需要該目錄,該目錄必須是穩定的,系統重啟后都繼續存在

-dc:該標記控制agent運行的datacenter的名稱,默認是dc1

-encrypt:指定secret key,使consul在通訊時進行加密,key可以通過consul keygen生成,同一個集群中的節點必須使用相同的key

-http-port:HTTP API偵聽端口,默認端口8500,可以在環境變量中進行設置,非常有用,可以用於與Consul進行通訊

-join:加入一個已經啟動的agent的ip地址,可以多次指定多個agent的地址。如果consul不能加入任何指定的地址中,則agent會啟動失敗,默認agent啟動時不會加入任何節點。

-retry-join:和join類似,但是允許你在第一次失敗后進行嘗試。

-retry-interval:兩次join之間的時間間隔,默認是30s

-retry-max:嘗試重復join的次數,默認是0,也就是無限次嘗試

-log-level:consul agent啟動后顯示的日志信息級別。默認是info,可選:trace、debug、info、warn、err。跟蹤 調試 詳情 警告 錯誤,可以通過Consul monitor使用任何級別,也可以通過重啟加載新的配置級別

-node:節點在集群中的名稱,在一個集群中必須是唯一的,默認是該節點的主機名(代表一個機器)

-pid-file:提供一個路徑來存放pid文件,可以使用該文件進行SIGINT/SIGHUP(關閉/更新)agent

-protocol:consul使用的協議版本 consul -v

-rejoin:使consul忽略先前的離開,在再次啟動后仍舊嘗試加入集群中。

-server:定義agent運行在server模式還是Client模式,提供時即為Server端,每個集群至少有一個server並且每台機器上不要超過5個dataceter.所有服務器采用一致性算法Raft保證數據一致,確保在故障的情況下的可用性。

-syslog:開啟系統日志功能,只在linux/osx上生效

-ui-dir:提供存放web ui資源的路徑,該目錄必須是可讀的
View Code

2、配置參數

Consul配置信心可以填寫在.json文件中,配置文件加載時會按照文件名字母排序的順序讀取,效果與命令行一致。

acl_datacenter:只用於server,指定的datacenter的權威ACL信息,所有的servers和datacenter必須同意ACL datacenter

acl_default_policy:默認是allow

acl_token:agent會使用這個token和consul server進行請求

acl_ttl:控制TTL的cache,默認是30s

addresses:一個嵌套對象,可以設置以下key:dns、http、rpc

advertise_addr:等同於-advertise

bootstrap:等同於-bootstrap

bootstrap_expect:等同於-bootstrap-expect

bind_addr:等同於-bind

ca_file:提供CA文件路徑,用來檢查客戶端或者服務端的鏈接

cert_file:必須和key_file一起

check_update_interval:

client_addr:等同於-client

datacenter:等同於-dc

data_dir:等同於-data-dir

disable_anonymous_signature:在進行更新檢查時禁止匿名簽名

disable_remote_exec:禁止支持遠程執行,設置為true,agent會忽視所有進入的遠程執行請求

disable_update_check:禁止自動檢查安全公告和新版本信息

dns_config:是一個嵌套對象,可以設置以下參數:allow_stale、max_stale、node_ttl 、service_ttl、enable_truncate

domain:默認情況下consul在進行DNS查詢時,查詢的是consul域,可以通過該參數進行修改

enable_debug:開啟debug模式

enable_syslog:等同於-syslog

encrypt:等同於-encrypt

key_file:提供私鑰的路徑

leave_on_terminate:默認是false,如果為true,當agent收到一個TERM信號的時候,它會發送leave信息到集群中的其他節點上。

log_level:等同於-log-level

node_name:等同於-node

ports:這是一個嵌套對象,可以設置以下key:dns(dns地址:8600)、http(http api地址:8500)、rpc(rpc:8400)、serf_lan(lan port:8301)、serf_wan(wan port:8302)、server(server rpc:8300)

protocol:等同於-protocol

rejoin_after_leave:等同於-rejoin

retry_join:等同於-retry-join

retry_interval:等同於-retry-interval

server:等同於-server

server_name:會覆蓋TLS CA的node_name,可以用來確認CA name和hostname相匹配

skip_leave_on_interrupt:和leave_on_terminate比較類似,不過只影響當前句柄

start_join:一個字符數組提供的節點地址會在啟動時被加入

syslog_facility:當enable_syslog被提供后,該參數控制哪個級別的信息被發送,默認Local0

ui_dir:等同於-ui-dir

verify_incoming:默認false,如果為true,則所有進入鏈接都需要使用TLS,需要客戶端使用ca_file提供ca文件,只用於consul server端,因為client從來沒有進入的鏈接

verify_outgoing:默認false,如果為true,則所有出去鏈接都需要使用TLS,需要服務端使用ca_file提供ca文件,consul server和client都需要使用,因為兩者都有出去的鏈接

watches:watch一個詳細名單
View Code

摘自:https://www.cnblogs.com/qidakang/p/7837519.html

-bind:為該節點綁定一個地址
-enable-script-checks=true:設置檢查服務為可用
-join:加入到已有的集群中
-server 表示當前使用的server模式
-node:指定當前節點在集群中的名稱 
-config-file - 要加載的配置文件
-config-dir:指定配置文件,定義服務的,默認所有以.json結尾的文件都會讀
-datacenter: 數據中心沒名稱,不設置的話默認為dc
-client: 客戶端模式
-ui: 使用consul自帶的ui界面 
-data-dir consul存儲數據的目錄
-bootstrap:用來控制一個server是否在bootstrap模式,在一個datacenter中只能有一個server處於bootstrap模式,當一個server處於bootstrap模式時,可以自己選舉為raft leader。
-bootstrap-expect:在一個datacenter中期望提供的server節點數目,當該值提供的時候,consul一直等到達到指定sever數目的時候才會引導整個集群,該標記不能和bootstrap公用

這兩個參數十分重要, 二選一,如果兩個參數不使用的話,會出現就算你使用join將agent加入了集群仍然會報 
[ERR] agent: failed to sync remote state: No cluster leader


ui: 相當於-ui 命令行標志。
acl_token:agent會使用這個token和consul server進行請求
acl_ttl:控制TTL的cache,默認是30s
addresses:一個嵌套對象,可以設置以下key:dns、http、rpc
advertise_addr:等同於-advertise
bootstrap:等同於-bootstrap
bootstrap_expect:等同於-bootstrap-expect
bind_addr:等同於-bindca_file:提供CA文件路徑,用來檢查客戶端或者服務端的鏈接
cert_file:必須和key_file一起
check_update_interval:
client_addr:等同於-client
datacenter:等同於-dc
data_dir:等同於-data-dir
disable_anonymous_signature:在進行更新檢查時禁止匿名簽名
enable_debug:開啟debug模式
enable_syslog:等同於-syslog
encrypt:等同於-encrypt
key_file:提供私鑰的路徑
leave_on_terminate:默認是false,如果為true,當agent收到一個TERM信號的時候,它會發送leave信息到集群中的其他節點上。
log_level:等同於-log-level node_name:等同於-node 
ports:這是一個嵌套對象,可以設置以下key:dns(dns地址:8600)、http(http api地址:8500)、rpc(rpc:8400)、serf_lan(lan port:8301)、serf_wan(wan port:8302)、server(server rpc:8300) 
protocol:等同於-protocol
rejoin_after_leave:等同於-rejoin
retry_join:等同於-retry-join
retry_interval:等同於-retry-interval 
server:等同於-server
syslog_facility:當enable_syslog被提供后,該參數控制哪個級別的信息被發送,默認Local0
ui_dir:等同於-ui-dir
View Code

 

現在手中資源有:4台linux和2台windows server

Linux:

   43.243.49.55

   43.243.49.60

  103.250.12.20

   39.105.144.51(阿里雲)

windows:

   43.243.49.57

  129.28.195.120(騰訊雲)

 

分配:

service服務器:(43.243.49.55,43.243.49.60,103.250.12.20)

client:(129.28.195.120)

api部署兩個,用於測試負載:(43.243.49.57,39.105.144.51)

 

linux上我都是基於docker部署,畢竟docker方便

首先部署所有的server端

43.243.49.55上面執行:

docker run --name s1 -d --restart=always -p 8500:8500 -p 8300:8300 -p 8301:8301 -p 8302:8302 -p 8301:8301/udp -p 8302:8302/udp -p 8600:8600 
consul agent -server -node s1 -bootstrap-expect 3 -advertise 43.243.49.55 --bind 0.0.0.0 -client=0.0.0.0 -ui -datacenter=dc-test

 

通過docker s1 logs 可以看到consul 已經運行成功,但提示沒有leader,因為設置了,-bootstrap-expect 3

這個集群需要3台server,所以要等3台server加如集群后才會選舉一個leader

 

 

 

 

 現在可以嘗試訪問:http://43.243.49.55:8500  會出現異常,因為集群中沒有3台server,沒有leader

 

 

 

 

43.243.49.60上面執行:

docker run --name s1 -d --restart=always -p 8500:8500 -p 8300:8300 -p 8301:8301 -p 8302:8302 -p 8301:8301/udp -p 8302:8302/udp -p 8600:8600 
consul agent -server -node s2 -bootstrap-expect 3 -advertise 43.243.49.60  --bind 0.0.0.0 -client=0.0.0.0 -join 43.243.49.55  -datacenter=dc-test

注意上面的-join 43.243.49.55,是加入集群,這里沒有-ui ,因為我不想把60開通ui功能,具體根據自己需求來

如果執行命令的時候,沒有join。也就是沒有加入任何集群,如果想加入集群,可以手動加入: docker exec -t s1 consul join 43.243.49.55 

 

 

 

 

 

103.250.12.20上面執行:

docker run --name s1 -d --restart=always -p 8500:8500 -p 8300:8300 -p 8301:8301 -p 8302:8302 -p 8301:8301/udp -p 8302:8302/udp 
-p 8600:8600 consul agent -server -node s3 -bootstrap-expect 3 -advertise 103.250.12.20 --bind 0.0.0.0 -client=0.0.0.0 -join 43.243.49.55 -datacenter=dc-test

 

這樣3台server都已經部署完成,通過docker logs s1 (任意一台server都可以)可以看到已經選舉了leader為s1節點,s2和s3為跟隨節點Follower

 

 

 

通過命令: docker exec -t s1 consul members 可以查看集群節點信息,Status狀態正常,Type為server

 

 

 

通過: docker exec -t s1 consul operator raft list-peers 查看集群信息,可以看到集群中的leader為note為s1的節點,ip為:43.243.49.55

 

 

 

還有很多命令比如: docker exec -t s1 consul members -detailed 

 

 

 

因為3台server,只有43.243.49.55開通了ui,嘗試訪問,集群部署成功

 

 

 

 

 3台節點也正常

 

 

 

 接下來搭建client端

因為129.28.195.120是windows server,所以不能跑docker

首先下載consul是一個exe運行程序

 

 

 

 打開cmd,切換到當前目錄,執行命令:

consul agent -ui -advertise 129.28.195.120 -bind=0.0.0.0 -client=0.0.0.0 
-data-dir C:\consul -config-dir=C:\consul\conf -node=c1 -join 43.243.49.55 -datacenter=dc-test
-data-dir是當前consul路徑,
-config-dir是配置文件,也就是配置服務的,后面會說

 

 

 

 

 

 執行成功了,再一次查看集群信息。多了一個client

 

 

 

 

 

 

 因為 129.28.195.120開通了ui,所以同樣可以訪問

 

 

 

 好了。接下來3個server和1個client的集群就搭建成功了,接下來就是服務的注冊了

服務注冊有2種方式:

1:直接配置文件的方式,也就是上面-config-dir的目錄

2:通過代碼http調用consul接口方式注冊

 -config-dir是上面部署widows的方式

如果是docker 跑client,需要掛載文件

--數據掛載方式
/consul/data:持久化數據存儲
/consul/config:配置文件

可以這樣:

docker run -d -p 8500:8500 --restart=always -v=/volume/consul/data:/consul/data -v=/volume/consul/config:/consul/config --name c2 
consul agent -ui -bind=0.0.0.0 -client 0.0.0.0 -node=c2 -join 43.243.49.55 -datacenter=dc-test

 

 那么寫幾個api試試,既然有2中方式注冊服務,那么都試試,

還有,安裝上面的計划,分別在43.243.49.57,39.105.144.51上部署,因為都是linux,我依然選擇用docker方式

准備工作

待會為了測試在不同的服務器跑負載,我這里創建2個api,分別用不同方式注冊,來比較

1:Order.Api(用配置文件的方式注冊)

2:User.Api(用代碼方式注冊)

 

 

 

2個api分別添加檢查檢查方法,該方法返回ok。說明服務器沒有掛掉

 

 

 

 

 為了區分不同的服務器,2個api分別添加返回當前服務器ip地址的方法,僅僅是為了測試,不用過多糾結

 

 

 

 

User.Api添加配置文件:這里填寫client地址,當然你填寫任意一台server地址也是可以的

看到網上有人提問說,只有一個client,如果這個client掛了。那不就注冊失敗了嗎?怎么解決,

我認為這擔心是多余的。掛了肯定會注冊失敗,client掛了。修復即可,

就好比我們一個項目部署在服務器,如果服務器不掛,你也不會老是擔心,如果服務器掛了怎么辦吧,比如,機房斷電。線路故障

這些也只能等待修復,如果硬是擔心。那就只能部署多個client,然后輪詢判斷??

{
  "ServiceDiscovery": {
    "ServiceName": "UserService",
    "Consul": {
      "HttpEndpoint": "http://129.28.195.120:8500",
      "DnsEndpoint": {
        "Address": "129.28.195.120",
        "Port": 8600
      }
    }
  }
}

然后配置對應的實體類,我這里就不寫了

 

 在User.Api 添加consul包

 

 

 在ConfigureServicesz中配置如下:

  public void ConfigureServices(IServiceCollection services)
        {
                 services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            //services.AddOptions();
            services.Configure<ServiceDiscoveryOptions>(Configuration.GetSection("ServiceDiscovery"));

            services.AddSingleton<IConsulClient>(p => new ConsulClient(cfg =>
            {
                var serviceConfiguration = p.GetRequiredService<IOptions<ServiceDiscoveryOptions>>().Value;

                if (!string.IsNullOrEmpty(serviceConfiguration.Consul.HttpEndpoint))
                {
                    // if not configured, the client will use the default value "127.0.0.1:8500"
                    cfg.Address = new Uri(serviceConfiguration.Consul.HttpEndpoint);
                }
            }));

        }

Configure配置如下:

 public void Configure(IApplicationBuilder app, IHostingEnvironment env,
            IApplicationLifetime lifetime,
        ILoggerFactory loggerFactory,
        IOptions<ServiceDiscoveryOptions> serviceOptions,
        IConsulClient consul)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            //app.UseHttpsRedirection();
            app.UseMvc();

            //InitTable(app);

            lifetime.ApplicationStarted.Register(() =>
            {
                RegisterService(app, lifetime, serviceOptions, consul);
            });
            //lifetime.ApplicationStopped.Register(OnStoped);
        }

 

然后是RegisterService注冊方法:

 private void RegisterService(IApplicationBuilder app, IApplicationLifetime appLife,
     IOptions<ServiceDiscoveryOptions> serviceOptions,
     IConsulClient consul)
        {
            var features = app.Properties["server.Features"] as FeatureCollection;
            var addresses = features.Get<IServerAddressesFeature>()
                .Addresses
                .Select(p => new Uri(p));

            foreach (var address in addresses)
            {
                var serviceId = $"{serviceOptions.Value.ServiceName}_{address.Host}:{address.Port}";

                //健康檢查
                var httpCheck = new AgentServiceCheck()
                {
                    DeregisterCriticalServiceAfter = TimeSpan.FromMinutes(1),
                    Interval = TimeSpan.FromSeconds(30),
                    HTTP = new Uri(address, "api/HealthCheck").OriginalString
                };

                //服務注冊
                var registration = new AgentServiceRegistration()
                {
                    Checks = new[] { httpCheck },
                    /*
                     測試的時候用 的localhost
                     那么在DnsClient取值的時候
                     AddressList會為空,不過HostName有值
                     */
                    Address = address.Host, // "127.0.0.1",
                    ID = serviceId,
                    Name = serviceOptions.Value.ServiceName,
                    Port = address.Port
                };

                consul.Agent.ServiceRegister(registration).GetAwaiter().GetResult();

                appLife.ApplicationStopping.Register(() =>
                {
                    consul.Agent.ServiceDeregister(serviceId).GetAwaiter().GetResult();
                });
            }
        }

 

因為是邊寫邊測試,所以先把User.Api 放到 服務器,43.243.49.57上試試效果,用Kestrel跑起來測試

執行命令: dotnet User.Api.dll --urls http://43.243.49.57:5001  當然5001端口得開通

 

運行成功后,可以看到服務注冊成功

 

 一切正常

 

 

直接訪問試試

 

稍后把Order.Api直接用配置文件的方式注冊,不過在這之前,講講服務的發現,

上面就是consul服務的注冊,那么看看服務發現該怎么做。

上面出現了8500和8600端口,8500是ui界面的。8600是DNS其實就是一個是注冊服務,一個是服務發現

 

 

創建一個Api:SD.Api (ServiceDiscovery簡稱)

服務發現需要依賴包:

 

 

 ConfigureServices配置

     public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            services.Configure<ServiceDiscoveryOptions>(Configuration.GetSection("ServiceDiscovery"));
            services.AddSingleton<IDnsQuery>(p =>
            {
                var serviceConfiguration = p.GetRequiredService<IOptions<ServiceDiscoveryOptions>>().Value;
                var dnsEndpoint = serviceConfiguration.Consul.DnsEndpoint;

                //推薦
                return new LookupClient(serviceConfiguration.Consul.DnsEndpoint.ToIPEndPoint());
                //或者
                //return new LookupClient(IPAddress.Parse(dnsEndpoint.Address), dnsEndpoint.Port);
                //return new LookupClient(IPAddress.Parse("127.0.0.1"), 8600);

            });

            //services.AddSingleton<IConsulClient>(p => new ConsulClient(cfg =>
            //{
            //    var serviceConfiguration = p.GetRequiredService<IOptions<ServiceDiscoveryOptions>>().Value;

            //    if (!string.IsNullOrEmpty(serviceConfiguration.Consul.HttpEndpoint))
            //    {
            //        // if not configured, the client will use the default value "127.0.0.1:8500"
            //        cfg.Address = new Uri(serviceConfiguration.Consul.HttpEndpoint);
            //    }
            //}));

            services.AddSingleton<HttpClient>();
        }
View Code

 

 創建Userservice控制器

 1   public class UserServiceController : ControllerBase
 2     {
 3         private HttpClient _httpClient;
 4         //private readonly string _userServiceUrl = "http://localhost:5000";
 5         private readonly string _userServiceUrl;
 6         private readonly IDnsQuery _dns;
 7         private readonly IOptions<ServiceDiscoveryOptions> _options;
 8         public UserServiceController(HttpClient httpClient, IDnsQuery dns, IOptions<ServiceDiscoveryOptions> options)
 9         {
10             _httpClient = httpClient;
11             _dns = dns ?? throw new ArgumentNullException(nameof(dns));
12             _options = options ?? throw new ArgumentNullException(nameof(options));
13 
14             var result = _dns.ResolveService("service.consul", _options.Value.ServiceName);
15             /*
16              如果服務注冊用的是localhost,那么AddressList為空,則取HostName
17              必須是ip地址,比如127.0.0.1
18              */
19             var addressList = result.First().AddressList;
20             //var address = result.First().AddressList.FirstOrDefault();
21 
22             var address = addressList.Any() ?
23                 addressList.FirstOrDefault().ToString() :
24                 result.First().HostName;
25             var port = result.First().Port;
26 
27             _userServiceUrl = $"http://{address}:{port}";
28         }
29 
30         [HttpGet]
31         public async Task<string> Get()
32         {
33             var service = await _dns.ResolveServiceAsync("service.consul", _options.Value.ServiceName);
34             var address = service.First().AddressList.FirstOrDefault();
35             var port = service.First().Port;
36 
37             var response = await _httpClient.GetAsync(_userServiceUrl + "/api/users");
38             if (response.StatusCode == HttpStatusCode.OK)
39             {
40                 var result = await response.Content.ReadAsStringAsync();
41                 return result;
42             }
43             return string.Empty;
44         }
45     }
View Code

 

 運行執行成功

 

 截至目前服務的注冊和發現已經介紹完成了,那么最后來看看配置方式:

現在只有43.243.49.57這台服務器部署了User.Api

接下來的工作是

1:把Order.Api也部署在43.243.49.57上。用配置文件的方式注冊服務

2:在39.105.144.51上用docker部署User.Api和Order.Api,用配置文件的方式注冊服務

既然是docker。就必須要寫dockerfile,既然是多個一起跑。就免不了docker-compose

這里就不放dockerfile了。后面源碼會有,

直接放docker-compose

version: '3'

services:
  order.api:
    build: 
      context: Order.Api
      dockerfile: dockerfile
    container_name: 'orderapi'
    restart: always
    volumes:
      - /volume/api/OrderApi/appsettings.json:/app/appsettings.json
    ports:
      - '6002:80'
  user.api:
    build: 
      context: User.Api
      dockerfile: dockerfile
    container_name: 'userapi'
    restart: always
    volumes:
      - /volume/api/UserApi/appsettings.json:/app/appsettings.json
    ports:
      - '6001:80'
這里注意下:
數據掛載:appsettings.json 要提前創建,因為是映射的文件,如果當前不存在,會變成文件夾
平常我們一個項目打包的時候,一般都是docker-compose.yml和dockerfile在同一個目錄的,
但這里是打包多個項目,要區分好路徑,我這里的
docker-compose.yml是在項目最外層的


dockerfile
:是項目的dockerfile 路徑
context:是上下文的路徑 所以 dockerfile 必須在 User.Api下 即:User.Api/dockerfile 就是當前項目下 的路徑

 

當我docker-compose up 的時候遇到了一個錯誤

 

 

 

大體意思就是地址無效,

如果大家是跟着上面來的。應該會記得,我在演示User.Api項目的時候是通過代碼調用consul http api 注冊服務的,

部署在了43.243.49.57上,我並沒有把代碼注釋掉,當然,我數據掛載也是配置了文件的client注冊地址的

 

 

 

 

既然遇到了,那么我們就一起來解決下,本來是想在39.105.144.51上的api都是通過配置注冊的。

那就這樣吧:39.105.144.51上的api,userapi通過http api注冊,orderapi通過服務方式

因為在http api注冊的時候,是通過獲取程序運行的ip和端口注冊的

 

 

 我們在43.243.49.57 上跑userapi  是這樣的,還記得吧 dotnet User.Api.dll --urls http://43.243.49.57:5001

當你沒有指定程序運行的ip和端口肯定就是無效的,而拋出異常

這里要說明下:userapi只是注冊失敗,並不代表程序沒跑起來

 

 

 訪問也是成功的

 

 

 只是服務注冊失敗

 

 

 

 

嘗試過幾種方法都不行

比如:UseUrls("http://39.105.144.51:90")會提示 Cannot assign requested address,查了下資料好像是linux分配地址問題

當然這樣就更不行了 UseUrls("http://*:6001")外網是可以訪問,但注冊是不行的

 

既然這樣。那就不折騰了。都通過過配置的方式吧

用docker部署userapi和orderapi后,編寫配置文件

{
    "services":[
        {
            "ID":"UserService_39.105.144.51:6001",
            "Name":"UserService",
            "Tags":[
                "UserApi",
                "獲取用戶信息"
            ],
            "Address":"39.105.144.51",
            "Port":6001,
            "Check":{
                "HTTP":"http://39.105.144.51:6001/api/HealthCheck",
                "Interval":"10s",
                "DeregisterCriticalServiceAfter":"1m"
            }
        },
        {
            "id":"OrderService_39.105.144.51:6002",
            "name":"OrderService",
            "tags":[
                "獲取訂單信息"
            ],
            "Address":"39.105.144.51",
            "Port":6002,
            "Check":[
                {
                    "HTTP":"http://39.105.144.51:6002/api/HealthCheck",
                    "Interval":"10s",
                    "DeregisterCriticalServiceAfter":"1m"
                }
            ]
        }
    ]
}

129.28.195.120是client,上面配置過
-config-dir=C:\consul\conf 

那么就把這個json文件放到這個目錄下,里面可以多個,

比如:order.json user.json  這樣就可以根據名字來區分服務,修改起來也方便,consul啟動的時候,會讀取所有json文件

 

 

 

重啟consul,查看webui頁面

 

 

 

單擊UserService進去,可以看到注冊了2個服務,分別在不通的機器上

 

 

 這樣一個完整的consul集群就搭建好了

網關這一塊 Gateway 用ocelot

添加nuget包:Ocelot.Provider.Consul

它包含了Consul,Ocelot,Ocelot.Provider.Consul

如果你是單獨安裝了Ocelot,那么就還得安裝Ocelot.Provider.Consul

 

 

 創建一個Ocelot.json,寫入下面配置

測試下負載均衡,因為userapi有2台,所以配置2個

 

{
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/api/users",
      "DownstreamScheme": "http",
      "LoadBalancerOptions": {
        "Type": "LeastConnection"
      },
      "DownstreamHostAndPorts": [
        {
          "Host": "39.105.144.51",
          "Port": 6001
        },
        {
          "Host": "43.243.49.57",
          "Port": 5001
        }
      ],
      "UpstreamPathTemplate": "/users",
      "UpstreamHttpMethod": [ "Get" ]
      //"AuthenticationOptions": {
      //  "AuthenticationProviderKey": "TestKey",
      //  "AllowedScopes": []
      //}
    }
  ],
  "GlobalConfiguration": {
    //這是暴露給外部的url
    "BaseUrl": "http://localhost"
  }
}

 

 

然后注冊:

services.AddOcelot().AddConsul();

 app.UseOcelot();

加載Ocelot.json配置,暴露81端口

 

 

 

運行測試,輸入 http://localhost:81/users 多次刷新訪問

會在2台電腦之前切換訪問,達到了負載均衡的效果

 

 

 

 

 

 源碼:

https://github.com/byniqing/ConsulExample

 

Ocelot知識遠不止這些,這里就不說了。具體自己看文檔吧,

參考:

http://www.jessetalk.cn/2018/03/19/net-core-apigateway-ocelot-docs/

https://www.cnblogs.com/bossma/p/9756809.html

https://blog.csdn.net/weixin_33953249/article/details/88759709

https://www.cnblogs.com/RainingNight/p/servicediscovery-consul-in-asp-net-core.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM