consul java connect_accumulation
以consul為微服務治理中心的微服務架構演示
方法論
前端組件化(可視化工具)
后端微服務化(分布式統一管控)
迭代自動化(CI/CD)
過程流水線化(DevOps)
定義接口入/出參后,通過KV模擬數據即可實現前后端聯調,再深入開發相關微服務
本工程完整演示了以consul為微服務治理中心的標準微服務架構各個基本模塊功能,通過該項目能夠完整了解微服務注冊、發現、健康監測、負載均衡、全鏈路監控、配置中心、權限控制等。
consul集群部署說明
修改配置文件,存放目錄為consul.d,后綴為.json,可以有多個配置文件,后面的屬性覆蓋前面的。
1.公共基礎配置文件/etc/consul.d/base-config.json
{
"ports": {
"http": 8500,
"dns": 8600,
"serf_lan": 8301,
"serf_wan": 8302,
"server": 8300
}
}
2.server節點的ACL配置文件acl.json
{
"datacenter": "dc1",
"acl_datacenter": "dc1",
"acl_master_token": "6407e6d8-1696-4b98-826d-0ad9a5c93449",
"acl_default_policy": "deny",
"server": true,
"log_level": "DEBUG",
"bootstrap_expect": 3,
"client_addr": "0.0.0.0"
}
3.client節點的ACL配置文件acl.json
{
"datacenter": "dc1",
"acl_datacenter": "dc1",
"acl_master_token": "6407e6d8-1696-4b98-826d-0ad9a5c93449",
"acl_token": "dd2c1eb3-7698-efb1-d213-f84d40fb5970",
"acl_default_policy": "deny",
"server": false,
"log_level": "DEBUG",
"client_addr": "0.0.0.0"
}
備注:
先啟動server
瀏覽器打開UI,用acl_master_token設置ACL
創建一個新的ACL
將token配置到client的ACL文件的acl_token
逐步啟動client
這個acl_token就是java bootstrap.yml中的consul配置
以docker方式啟動consul
1.啟動主服務server1
docker run -d --net host -p 8600:8600 -p 8500:8500 -p 8600:53/udp --name consul-server -v /etc/consul.d:/etc/consul.d consul:latest agent -bind=172.16.15.233 -config-dir=/etc/consul.d -node=server1 -ui
2.啟動主服務server2
docker run -d --net host -p 8600:8600 -p 8500:8500 -p 8600:53/udp --name consul-server -v /etc/consul.d:/etc/consul.d consul:latest agent -bind=172.16.15.237 -config-dir=/etc/consul.d -node=server2 -retry-join=172.16.15.233
3.啟動主服務server3
docker run -d --net host -p 8600:8600 -p 8500:8500 -p 8600:53/udp --name consul-server -v /etc/consul.d:/etc/consul.d consul:latest agent -bind=172.16.15.232 -config-dir=/etc/consul.d -node=server3 -retry-join=172.16.15.233
4.啟動客戶端client1
docker run -d --net host -p 8600:8600 -p 8500:8500 -p 8600:53/udp --name consul-client -v /etc/consul.d:/etc/consul.d consul:latest agent -bind=172.16.15.230 -config-dir=/etc/consul.d -node=client1 -retry-join=172.16.15.233
5.啟動客戶端client2
docker run -d --net host -p 8600:8600 -p 8500:8500 -p 8600:53/udp --name consul-client -v /etc/consul.d:/etc/consul.d consul:latest agent -bind=172.16.15.231 -config-dir=/etc/consul.d -node=client2 -retry-join=172.16.15.233
微服務模塊
Springcloud 接入consul
基本依賴包
參考父工程的pom文件
把Spring consul的配置存放到bootstrap.yml文件里,而不是application.yml里邊
spring:
cloud:
consul:
host: 172.28.50.28
port: 8500
discovery:
register: true
prefer-ip-address: true
instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
acl-token: dd2c1eb3-7698-efb1-d213-f84d40fb5970
config:
enabled: true
format: yaml
watch:
enabled: true
prefix: config
data-key: data
網關服務,在spring注入的時候,要把需要引入的組件注入到主應用里邊
//注冊到consul的注入項
@EnableDiscoveryClient
//引入zuul進行網關路由配置的注入項
@EnableZuulProxy
@SpringBootApplication
業務集成服務,該服務是實現復雜業務邏輯的主體服務,是直接對接網關的微服務,是發現和調用其他微服務的主體服務
//在主應用注入微服務發現注入項
@EnableFeignClients
//建立一系列的接口類,通過接口類實現對其他微服務的調用
利用consul KV靜態、動態管理配置,consul也是一個配置中心,通過KV管理配置屬性
在consul KV中配置key的前綴為config(和consul.config.prefix屬性值一致),下級目錄為微服務名稱,用逗號隔開為不同環境,以consul.config.data-key屬性結尾,例如:config/accumulation-api-gateway,dev/data作為微服務accumulation-api-gateway的開發環境配置文件
如果是靜態配置屬性,靜態配置只在微服務重啟的時候才會從consul KV獲取一次,例如:RabbitMQ的配置
如果要動態獲取配置屬性,則必須編寫配置屬性bean,動態配置會在consul KV更新后自動同步到微服務的對應bean上,例如:Redis的配置
api網關微服務api-gateway
路由配置、鏈路監控配置存儲到consul的key/value中,路由指向業務邏輯層
#健康監控配置
management:
health:
redis:
enabled: false
consul:
enabled: true
#feign配置
zuul:
routes:
four-operations:
path: /api/**
serviceId: accumulation-business-layer
ribbon:
ReadTimeout: 120000
ConnectTimeout: 300000
#鏈路跟蹤sleuth & zipkin配置
spring:
zipkin:
base-url: http://172.28.43.90:9411
sleuth:
sampler:
percentage: 1.0
業務邏輯層微服務business-layer
該微服務實現全部業務輸出接口,對輸入數據進行必要的合法性檢測,解決跨域調用問題等
在controler層對需要檢測的對象注入@Valid
@CrossOrigin
@RequestMapping(value = "/login",method = RequestMethod.POST)
@ApiOperation(value = "注冊用戶",notes = "注冊一個用戶到系統中")
@ResponseBody
public String login(@RequestBody @Valid RegisterVO user) throws IOException {
log.info("參數={}",user);
//該用戶是否已經注冊
String accessToken=iSso.login(user.getUsername(),user.getPassword());
log.info("accessToken={}",accessToken);
JSONObject result=new JSONObject();
result.put("code",1000);
result.put("email",user.getUsername());
result.put("accessToken",accessToken);
return result.toJSONString();
}
需要檢測的對象注解正則式、提示信息等
public class RegisterVO {
@NotBlank(message = "用戶名不能為空")
@Pattern(regexp = "^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*\\.[a-zA-Z0-9]{2,6}$",message = "必須為郵箱地址")
private String username;
@NotBlank(message = "密碼不能為空")
@Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$",message = "密碼必須為數字、字母混合")
@Length(min = 8,max = 16,message = "密碼長度必須為8-16個字符")
private String password;
}
調用文件上傳微服務,在接口中要指定consumes為MediaType.MULTIPART_FORM_DATA_VALUE,參數注解為@RequestPart,否則調用出錯
@FeignClient(name = "accumulation-distribution-file-layer")
@RequestMapping("/file")
public interface IDistributionFile {
@PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
String save(@RequestPart(value = "fileName") MultipartFile file);
@RequestMapping(value = "/download/{fileName}", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
byte[] get(@PathVariable("fileName") String fileName);
}
緩存微服務層cache-layer
通過jedis實現對redis集群的使用
在主類Application添加配置注解,解決autowire注入失敗問題
//解決autowire注入失敗問題
@EnableConfigurationProperties({RedisConfig.class})
redis配置從consul key/value動態獲取,通過RedisConfig實例化redis連接池單例bean
#redis配置
redis:
host: 172.28.19.200
port: 7389
password:
timeout: 100
maxActive: 200
maxIdle: 200
minIdle: 5
maxWaitMillis: 10240
expireSeconds: 36000
commandTimeout: 10240
clusterNodes:
數據庫持久層database-layer
該模塊通過mybatis、mongodb演示了mysql關系型數據庫及mongodb文檔型數據庫的使用方法
數據庫連接配置
#數據庫連接
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://172.28.5.92:3306/ToonBeacon?useUnicode=true&characterEncoding=utf8
username: syswin
password: syswin
maxActive: 2335
maxIdel: 120
maxWait: 100
#mongodb配置
data:
mongodb:
uri: mongodb://172.28.43.18:27017/crazyicelee
通過注解實現mysql數據庫的VO映射,直接將sql注入到相關接口類中
@Mapper
public interface StudentDao {
@Results({
@Result(property = "name", column = "name"),
@Result(property = "age", column = "age"),
@Result(property = "sex", column = "sex"),
@Result(property = "email",column = "email")
})
@Select("SELECT * FROM student WHERE age > #{age}")
List searchAge(int age);
@Insert("INSERT INTO student(name, age,sex,email) VALUES (#{name}, #{age},#{sex},#{email})")
void addStudent(Student user);
@Update("UPDATE student SET name=#{name},sex=#{sex},age=#{age} WHERE email=#{email}")
void updateStudent(Student student);
@Delete("DELETE FROM student WHERE email=#{email}")
void deleteStudent(String email);
}
mongdb直接擴展MongoRepository作為接口實現數據庫的訪問
@Service
public interface UserRepository extends MongoRepository {
}
利用MongoDB 的GridFS分布式存儲文件
4.1 創建GridFS的GridFSBucket bean
@Configuration
public class MongoConf {
@Autowired
private MongoDbFactory mongoDbFactory;
@Bean
public GridFSBucket getGridFSBuckets() {
MongoDatabase db = mongoDbFactory.getDb();
return GridFSBuckets.create(db);
}
}
4.2 實現文件上傳、下載、刪除API
@RestController
@RequestMapping("/file")
public class GridFSController {
private static Logger LOGGER = LoggerFactory.getLogger(GridFSController.class);
@Autowired
private GridFsTemplate gridFsTemplate;
@Autowired
private GridFSBucket gridFSBucket;
@PostMapping(value = "/upload")
public String save(@RequestParam(value = "fileName") MultipartFile file) {
LOGGER.info("Saving file..");
DBObject metaData = new BasicDBObject();
metaData.put("createdDate", new Date());
String fileName = UUID.randomUUID().toString();
try {
InputStream inputStream = file.getInputStream();
gridFsTemplate.store(inputStream, fileName, file.getContentType(), metaData);
} catch (IOException e) {
LOGGER.error("IOException: " + e);
throw new RuntimeException("System Exception while handling request");
}
LOGGER.info("File return: " + fileName);
return fileName;
}
@RequestMapping(value = "/download", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] get(@RequestParam(value = "fileName") String fileName) throws IOException {
LOGGER.info("Getting file.." + fileName);
GridFSFile result = gridFsTemplate.findOne(new Query().addCriteria(Criteria.where("filename").is(fileName)));
if (result == null) {
LOGGER.info("File not found" + fileName);
throw new RuntimeException("No file with name: " + fileName);
}
LOGGER.info("File found " + fileName);
//打開流下載對象
GridFSDownloadStream downloadStream = gridFSBucket.openDownloadStream(result.getObjectId());
//獲取流對象
GridFsResource gridFsResource=new GridFsResource(result,downloadStream);
return IOUtils.toByteArray(gridFsResource.getInputStream());
}
@RequestMapping(value = "/delete", method = RequestMethod.DELETE)
public void delete(@RequestParam(value = "fileName") String fileName) {
LOGGER.info("Deleting file.." + fileName);
gridFsTemplate.delete(new Query().addCriteria(Criteria.where("filename").is(fileName)));
LOGGER.info("File deleted " + fileName);
}
}
消息隊列接入層message-layer
本模塊實現了接入rabbitmq的消息發送、接收,通過注解方式實現
#RabbitMq配置
spring:
rabbitmq:
host: 172.28.19.123
port: 5672
username: guest
password: guest
virtual-host: /
publisher-confirms: true
單點登錄微服務層sso-layer
本模塊以JWT演示了用戶登錄token的實現,可以將token存儲到redis進行全站認證
基于OAuth2框架實現SSO
用mysql作為用戶體系數據存儲系統,mybatis作為數據庫連接器,以SQL注解到DAO接口的方式實現簡單的數據庫訪問。
1.1 在啟動主類添加Mapper掃碼注解,指定數據庫映射類所在包,并且要注解@EnableWebSecurity,標明該服務提供OAuth2認證
@SpringBootApplication
@EnableAuthorizationServer
@EnableWebSecurity
@MapperScan(basePackages = "com.crazyice.lee.accumulation.sso.dao")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
1.2 SQL注解到接口實現數據庫訪問
這里特別說明SQL的IN操作實現方法,該接口必須添加@Mapper、@Repository注解,否則可能導致@Autowired失效或者創建Bean失敗
@Mapper
@Repository
public interface SysPermissionServiceDao {
@Results({
@Result(property = "id", column = "id"),
@Result(property = "pid", column = "pid"),
@Result(property = "type", column = "type"),
@Result(property = "name",column = "name"),
@Result(property = "code",column = "code"),
@Result(property = "uri",column = "uri"),
@Result(property = "seq",column = "seq"),
@Result(property = "createUser",column = "create_user"),
@Result(property = "createTime",column = "create_time"),
@Result(property = "updateUser",column = "update_user"),
@Result(property = "updateTime",column = "update_time")
})
@Select({"
"SELECT * FROM sys_permission WHERE id IN "+
""+
"#{id}"+
""+
""
})
List findByIds(@Param("ids") List ids);
}
1.3 實現WebSecurityConfigurerAdapter適配器
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DemoUserDetailsService demoUserDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(demoUserDetailsService).passwordEncoder(passwordEncoder());
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**", "/css/**", "/images/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login")
.and()
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest()
.authenticated()
.and().csrf().disable().cors();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new SCryptPasswordEncoder();
}
}
1.4 實現UserDetailsService接口
@Service
public class DemoUserDetailsService implements UserDetailsService {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoUserDetailsService.class);
private static final PasswordEncoder passwordEncoder=new SCryptPasswordEncoder();
@Autowired
private SysUserServiceDao sysUserServiceDao;
@Autowired
private PermissionService permissionService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根據用戶名從數據庫獲取用戶信息
SysUser sysUser = sysUserServiceDao.getByUsername(username);
if (null == sysUser) {
LOGGER.warn("用戶:{},不存在", username);
throw new UsernameNotFoundException(username);
}
//根據用戶id獲取用戶權限
List permissionList = permissionService.findByUserId(sysUser.getId());
List authorityList = new ArrayList<>();
permissionList.stream().forEach(item->authorityList.add(new SimpleGrantedAuthority(item.getCode())));
//生成用戶信息進行口令驗證
User user = new User(sysUser.getUsername(), passwordEncoder.encode(sysUser.getPassword()), authorityList);
LOGGER.info("登錄用戶: {}", JSON.toJSONString(user));
return user;
}
}
實現OAuth2認證服務
自定義springboot注解演示模塊myself-annotation
本模塊以aop的方式實現了自定義注解的演示
websocket服務演示模塊
本模塊以websocket演示了websocket協議的服務端及客戶端,實現了服務端的連接監聽、消息監聽、消息發送、不在線用戶推送通知等,客戶端實現了新用戶加入、退出、和指定人聊天、群發等。
其他bean注入websocket方法
websocket的ServerEndpoint不能直接使用@Autowired將其他bean注入進來,而應該使用以下步驟
1.1 在主程序Application中使用靜態方法調用傳遞參數,代碼如下:
public class Application {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(Application.class);
ConfigurableApplicationContext configurableApplicationContext = springApplication.run(args);
//解決WebSocket不能注入的問題
WebSocketServer.setApplicationContext(configurableApplicationContext);
}
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
1.2 在websocket程序中定義需要注入的bean,代碼如下:
//此處是解決無法注入的關鍵
private static ApplicationContext applicationContext;
//注入的service
private static ISendNotice iSendNotice;
//注入動態配置
private static ThirdParam thirdParam;
//注入本地緩存service
private static CacheServer cacheServer;
1.3 在websocket程序中定義set方法接納參數,代碼如下:
//外部bean注入進來
public static void setApplicationContext(ApplicationContext applicationContext) {
WebSocketServer.applicationContext = applicationContext;
WebSocketServer.iSendNotice=applicationContext.getBean(ISendNotice.class);
WebSocketServer.thirdParam=applicationContext.getBean(ThirdParam.class);
WebSocketServer.cacheServer=applicationContext.getBean(CacheServer.class);
}
利用ehcache將用戶信息持久化緩存到本地
2.1 配置ehcache.xml,將eternal="true"
2.2 配置緩存策略服務CacheServer并注入進websocket中使用,cache key為字符串的時候要用單引號'括住
超級賬本fabric鏈代碼演示模塊
該模塊實現了運行在超級賬本區塊鏈上的鏈碼編寫,接入,相關接口對接等功能演示
快速數據字典查詢服務
該模塊以極簡的方式實現了通用數據自典查詢服務,利用consul KV的自動同步特性為分布式數據字典維護和查詢提供了極簡的解決方案。
將數據字典以KV的方式配置到consul平臺
給數據字典配置動態bean,該bean動態從consul KV獲取數據字典的MAP數據,存儲到內存中,所以在提供給微服務查詢時速度很快,解決了通常處理方案的讀取數據庫、加載緩存等中間過程,既保證了數據一致性,又保證了分布式服務的高可靠及內存訪問的高效率。
@ConfigurationProperties(prefix = "dictionary")
@Data
public class DictionaryConfig {
private Map sex;
private Map province;
private Map city;
}
備注:每添加一個字典,只需要在該代碼里邊增加一個Map屬性即可
通用的restFul API,該服務通過反射機制實現了一致的數據字典查詢,適用所有基于MAP數據格式的數據字典,而且可以根據Key的前綴進行級聯過濾。
@RestController
public class Controller {
private static Logger log = LoggerFactory.getLogger(Controller.class);
@Autowired
private DictionaryConfig dictionaryConfig;
//首字母轉大寫
public static String toUpperCaseFirstOne(String s){
if(Character.isUpperCase(s.charAt(0)))
return s;
else
return (new StringBuilder()).append(Character.toUpperCase(s.charAt(0))).append(s.substring(1)).toString();
}
@RequestMapping(value = "/getDictionary/{name}",method = RequestMethod.GET)
@ApiOperation(value = "字典",notes = "獲取指定字典的KV列表")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query",name="filter",value = "刪選key",dataType = "String")
})
public Map getDictionary(@PathVariable("name") String name, @RequestParam(value = "filter",name = "filter",required = false) String filter){
try {
Map totalMap=(Map) dictionaryConfig.getClass().getMethod("get" + toUpperCaseFirstOne(name)).invoke(dictionaryConfig);
if(filter==null) {
return totalMap;
}
else{
return totalMap.entrySet().stream().filter((e)->{
if(e.getKey().startsWith(filter))
return true;
else
return false;
}).collect(Collectors.toMap((e)->e.getKey(),(e)->e.getValue()));
}
} catch (IllegalAccessException e) {
log.error(e.getLocalizedMessage());
} catch (InvocationTargetException e) {
log.error(e.getLocalizedMessage());
} catch (NoSuchMethodException e) {
log.error(e.getLocalizedMessage());
}
return null;
}
}
個性化駕駛艙
該微服務以KV的方式提供個性化駕駛艙數據并配合前端聯調,通過這種模式能夠加速接口定義及聯調效率。
駕駛艙按照用戶個性化標識動態提供需要顯示的模塊數據,前端根據模塊數據及類型以相關樣式顯示這些模塊
通過配置consul的key/value來調整數據配合前端用戶交互,確定接口數據結構后就可以深入開發相關模塊的后端邏輯
每個模塊的數據為統一的list對象,再深入的數據結構可以根據模塊的實際情況自由定義,該數據映射為bean Model
##################
# 以下配置到consul #
##################
#健康監控配置
management:
health:
redis:
enabled: false
consul:
enabled: true
#個性化模塊列表
personal:
cockpits:
- userId: 13701231472
models:
- 1
- 3
- 4
- 5
- userId: 13691491904
models:
- 1
- 2
- 3
- 4
- 5
- userId: 18513125518
models:
- 3
- 4
- 5
#個性化模塊內容
models:
- id: 1
type: visit
name: 會議管理
data:
- name: 調查內容一
- name: 調查內容二
- id: 2
type: list
name: 調查信息
data:
- name: 調查內容一
- name: 調查內容二
- id: 3
type: content
name: 內容展示
data:
- name: 豐富的可視化類型 ECharts 提供了常規的折線圖、柱狀圖、散點圖、餅圖、K線圖,用于統計的盒形圖,用于地理數據可視化的地圖、熱力圖、線圖,用于關系數據可視化的關系圖、treemap、旭日圖,多維數據可視化的平行坐標,還有用于 BI 的漏斗圖,儀表盤,并且支持圖與圖之間的混搭。
- id: 4
type: app
name: 應用
data:
- name: 會議
visit: 122
img: http://scloud.toon.mobi/f/oCu-gQmNjNhDXnXOy8qPI8gPXQOM9kCn6mSLoRdYI+IfG.png
- name: 日程
visit: 122
img: http://scloud.toon.mobi/f/Mzu1uXTPFTo1mZaqY195QCdMGFhJRm47VPaPGVrB8d8fG.png
- name: 問卷調查
visit: 122
img: http://scloud.toon.mobi/f/VPTW828DPVYWOQEC6rgx-uThn2PRffr9OoRoR+Yk0WgfG.png
- id: 5
type: chart-loudou
name: 漏斗圖
data:
- name: 訪問
value: 20
- name: 資訊
value: 40
- name: 訂單
value: 60
- name: 點擊
value: 80
- name: 展示
value: 100
- id: 6
type: chart-zhexian
name: 一周信息統計
data:
- name: 訪問
value: 12
- name: 資訊
value: 19
- name: 訂單
value: 12
- name: 點擊
value: 15
- name: 展示
value: 18
本工程的完整源代碼
該工程代碼經過驗證均可正常運行。
碼云git下載地址
總結
以上是生活随笔為你收集整理的consul java connect_accumulation的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: Teams与OneDrive for B
- 下一篇: c/c++ accumulation
