consul java connect_accumulation
以consul為微服務治理中心的微服務架構(gòu)演示
方法論
前端組件化(可視化工具)
后端微服務化(分布式統(tǒng)一管控)
迭代自動化(CI/CD)
過程流水線化(DevOps)
定義接口入/出參后,通過KV模擬數(shù)據(jù)即可實現(xiàn)前后端聯(lián)調(diào),再深入開發(fā)相關(guān)微服務
本工程完整演示了以consul為微服務治理中心的標準微服務架構(gòu)各個基本模塊功能,通過該項目能夠完整了解微服務注冊、發(fā)現(xiàn)、健康監(jiān)測、負載均衡、全鏈路監(jiān)控、配置中心、權(quán)限控制等。
consul集群部署說明
修改配置文件,存放目錄為consul.d,后綴為.json,可以有多個配置文件,后面的屬性覆蓋前面的。
1.公共基礎(chǔ)配置文件/etc/consul.d/base-config.json
{
"ports": {
"http": 8500,
"dns": 8600,
"serf_lan": 8301,
"serf_wan": 8302,
"server": 8300
}
}
2.server節(jié)點的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節(jié)點的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設(shè)置ACL
創(chuàng)建一個新的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
網(wǎng)關(guān)服務,在spring注入的時候,要把需要引入的組件注入到主應用里邊
//注冊到consul的注入項
@EnableDiscoveryClient
//引入zuul進行網(wǎng)關(guān)路由配置的注入項
@EnableZuulProxy
@SpringBootApplication
業(yè)務集成服務,該服務是實現(xiàn)復雜業(yè)務邏輯的主體服務,是直接對接網(wǎng)關(guān)的微服務,是發(fā)現(xiàn)和調(diào)用其他微服務的主體服務
//在主應用注入微服務發(fā)現(xiàn)注入項
@EnableFeignClients
//建立一系列的接口類,通過接口類實現(xiàn)對其他微服務的調(diào)用
利用consul KV靜態(tài)、動態(tài)管理配置,consul也是一個配置中心,通過KV管理配置屬性
在consul KV中配置key的前綴為config(和consul.config.prefix屬性值一致),下級目錄為微服務名稱,用逗號隔開為不同環(huán)境,以consul.config.data-key屬性結(jié)尾,例如:config/accumulation-api-gateway,dev/data作為微服務accumulation-api-gateway的開發(fā)環(huán)境配置文件
如果是靜態(tài)配置屬性,靜態(tài)配置只在微服務重啟的時候才會從consul KV獲取一次,例如:RabbitMQ的配置
如果要動態(tài)獲取配置屬性,則必須編寫配置屬性bean,動態(tài)配置會在consul KV更新后自動同步到微服務的對應bean上,例如:Redis的配置
api網(wǎng)關(guān)微服務api-gateway
路由配置、鏈路監(jiān)控配置存儲到consul的key/value中,路由指向業(yè)務邏輯層
#健康監(jiān)控配置
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
業(yè)務邏輯層微服務business-layer
該微服務實現(xiàn)全部業(yè)務輸出接口,對輸入數(shù)據(jù)進行必要的合法性檢測,解決跨域調(diào)用問題等
在controler層對需要檢測的對象注入@Valid
@CrossOrigin
@RequestMapping(value = "/login",method = RequestMethod.POST)
@ApiOperation(value = "注冊用戶",notes = "注冊一個用戶到系統(tǒng)中")
@ResponseBody
public String login(@RequestBody @Valid RegisterVO user) throws IOException {
log.info("參數(shù)={}",user);
//該用戶是否已經(jīng)注冊
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 = "密碼必須為數(shù)字、字母混合")
@Length(min = 8,max = 16,message = "密碼長度必須為8-16個字符")
private String password;
}
調(diào)用文件上傳微服務,在接口中要指定consumes為MediaType.MULTIPART_FORM_DATA_VALUE,參數(shù)注解為@RequestPart,否則調(diào)用出錯
@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實現(xiàn)對redis集群的使用
在主類Application添加配置注解,解決autowire注入失敗問題
//解決autowire注入失敗問題
@EnableConfigurationProperties({RedisConfig.class})
redis配置從consul key/value動態(tài)獲取,通過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:
數(shù)據(jù)庫持久層database-layer
該模塊通過mybatis、mongodb演示了mysql關(guān)系型數(shù)據(jù)庫及mongodb文檔型數(shù)據(jù)庫的使用方法
數(shù)據(jù)庫連接配置
#數(shù)據(jù)庫連接
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
通過注解實現(xiàn)mysql數(shù)據(jù)庫的VO映射,直接將sql注入到相關(guān)接口類中
@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作為接口實現(xiàn)數(shù)據(jù)庫的訪問
@Service
public interface UserRepository extends MongoRepository {
}
利用MongoDB 的GridFS分布式存儲文件
4.1 創(chuàng)建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 實現(xiàn)文件上傳、下載、刪除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
本模塊實現(xiàn)了接入rabbitmq的消息發(fā)送、接收,通過注解方式實現(xiàn)
#RabbitMq配置
spring:
rabbitmq:
host: 172.28.19.123
port: 5672
username: guest
password: guest
virtual-host: /
publisher-confirms: true
單點登錄微服務層sso-layer
本模塊以JWT演示了用戶登錄token的實現(xiàn),可以將token存儲到redis進行全站認證
基于OAuth2框架實現(xiàn)SSO
用mysql作為用戶體系數(shù)據(jù)存儲系統(tǒng),mybatis作為數(shù)據(jù)庫連接器,以SQL注解到DAO接口的方式實現(xiàn)簡單的數(shù)據(jù)庫訪問。
1.1 在啟動主類添加Mapper掃碼注解,指定數(shù)據(jù)庫映射類所在包,并且要注解@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注解到接口實現(xiàn)數(shù)據(jù)庫訪問
這里特別說明SQL的IN操作實現(xiàn)方法,該接口必須添加@Mapper、@Repository注解,否則可能導致@Autowired失效或者創(chuàng)建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 實現(xiàn)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 實現(xiàn)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 {
//根據(jù)用戶名從數(shù)據(jù)庫獲取用戶信息
SysUser sysUser = sysUserServiceDao.getByUsername(username);
if (null == sysUser) {
LOGGER.warn("用戶:{},不存在", username);
throw new UsernameNotFoundException(username);
}
//根據(jù)用戶id獲取用戶權(quán)限
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;
}
}
實現(xiàn)OAuth2認證服務
自定義springboot注解演示模塊myself-annotation
本模塊以aop的方式實現(xiàn)了自定義注解的演示
websocket服務演示模塊
本模塊以websocket演示了websocket協(xié)議的服務端及客戶端,實現(xiàn)了服務端的連接監(jiān)聽、消息監(jiān)聽、消息發(fā)送、不在線用戶推送通知等,客戶端實現(xiàn)了新用戶加入、退出、和指定人聊天、群發(fā)等。
其他bean注入websocket方法
websocket的ServerEndpoint不能直接使用@Autowired將其他bean注入進來,而應該使用以下步驟
1.1 在主程序Application中使用靜態(tài)方法調(diào)用傳遞參數(shù),代碼如下:
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,代碼如下:
//此處是解決無法注入的關(guān)鍵
private static ApplicationContext applicationContext;
//注入的service
private static ISendNotice iSendNotice;
//注入動態(tài)配置
private static ThirdParam thirdParam;
//注入本地緩存service
private static CacheServer cacheServer;
1.3 在websocket程序中定義set方法接納參數(shù),代碼如下:
//外部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鏈代碼演示模塊
該模塊實現(xiàn)了運行在超級賬本區(qū)塊鏈上的鏈碼編寫,接入,相關(guān)接口對接等功能演示
快速數(shù)據(jù)字典查詢服務
該模塊以極簡的方式實現(xiàn)了通用數(shù)據(jù)自典查詢服務,利用consul KV的自動同步特性為分布式數(shù)據(jù)字典維護和查詢提供了極簡的解決方案。
將數(shù)據(jù)字典以KV的方式配置到consul平臺
給數(shù)據(jù)字典配置動態(tài)bean,該bean動態(tài)從consul KV獲取數(shù)據(jù)字典的MAP數(shù)據(jù),存儲到內(nèi)存中,所以在提供給微服務查詢時速度很快,解決了通常處理方案的讀取數(shù)據(jù)庫、加載緩存等中間過程,既保證了數(shù)據(jù)一致性,又保證了分布式服務的高可靠及內(nèi)存訪問的高效率。
@ConfigurationProperties(prefix = "dictionary")
@Data
public class DictionaryConfig {
private Map sex;
private Map province;
private Map city;
}
備注:每添加一個字典,只需要在該代碼里邊增加一個Map屬性即可
通用的restFul API,該服務通過反射機制實現(xiàn)了一致的數(shù)據(jù)字典查詢,適用所有基于MAP數(shù)據(jù)格式的數(shù)據(jù)字典,而且可以根據(jù)Key的前綴進行級聯(lián)過濾。
@RestController
public class Controller {
private static Logger log = LoggerFactory.getLogger(Controller.class);
@Autowired
private DictionaryConfig dictionaryConfig;
//首字母轉(zhuǎn)大寫
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的方式提供個性化駕駛艙數(shù)據(jù)并配合前端聯(lián)調(diào),通過這種模式能夠加速接口定義及聯(lián)調(diào)效率。
駕駛艙按照用戶個性化標識動態(tài)提供需要顯示的模塊數(shù)據(jù),前端根據(jù)模塊數(shù)據(jù)及類型以相關(guān)樣式顯示這些模塊
通過配置consul的key/value來調(diào)整數(shù)據(jù)配合前端用戶交互,確定接口數(shù)據(jù)結(jié)構(gòu)后就可以深入開發(fā)相關(guān)模塊的后端邏輯
每個模塊的數(shù)據(jù)為統(tǒng)一的list對象,再深入的數(shù)據(jù)結(jié)構(gòu)可以根據(jù)模塊的實際情況自由定義,該數(shù)據(jù)映射為bean Model
##################
# 以下配置到consul #
##################
#健康監(jiān)控配置
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
#個性化模塊內(nèi)容
models:
- id: 1
type: visit
name: 會議管理
data:
- name: 調(diào)查內(nèi)容一
- name: 調(diào)查內(nèi)容二
- id: 2
type: list
name: 調(diào)查信息
data:
- name: 調(diào)查內(nèi)容一
- name: 調(diào)查內(nèi)容二
- id: 3
type: content
name: 內(nèi)容展示
data:
- name: 豐富的可視化類型 ECharts 提供了常規(guī)的折線圖、柱狀圖、散點圖、餅圖、K線圖,用于統(tǒng)計的盒形圖,用于地理數(shù)據(jù)可視化的地圖、熱力圖、線圖,用于關(guān)系數(shù)據(jù)可視化的關(guān)系圖、treemap、旭日圖,多維數(shù)據(jù)可視化的平行坐標,還有用于 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: 問卷調(diào)查
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: 一周信息統(tǒng)計
data:
- name: 訪問
value: 12
- name: 資訊
value: 19
- name: 訂單
value: 12
- name: 點擊
value: 15
- name: 展示
value: 18
本工程的完整源代碼
該工程代碼經(jīng)過驗證均可正常運行。
碼云git下載地址
總結(jié)
以上是生活随笔為你收集整理的consul java connect_accumulation的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Teams与OneDrive for B
- 下一篇: 在Microsoft Teams中的Vi